Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wpcomsp-simple-events",
"version": "2.1.0",
"version": "2.1.1",
"description": "A simple Gutenberg-first event management plugin that integrates with WooCommerce Box Office.",
"author": {
"name": "WordPress.com Special Projects Team",
Expand Down
6 changes: 3 additions & 3 deletions plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Simple Events Plugin bootstrap file.
*
* @since 1.0.0
* @version 2.1.0
* @version 2.1.1
* @author WordPress.com Special Projects
* @license GPL-3.0-or-later
*
Expand All @@ -14,7 +14,7 @@
* Description: Event management frontend for WooCommerce Box Office.
* Requires at least: 6.5
* Tested up to: 6.9
* Version: 2.1.0
* Version: 2.1.1
* Requires PHP: 8.0
* Author: WordPress.com Special Projects
* Author URI: https://wpspecialprojects.wordpress.com
Expand All @@ -32,7 +32,7 @@
function_exists( 'get_plugin_data' ) || require_once ABSPATH . 'wp-admin/includes/plugin.php';
define( 'SE_METADATA', get_plugin_data( __FILE__, false, false ) );

define( 'SE_VERSION', '2.1.0' );
define( 'SE_VERSION', '2.1.1' );
define( 'SE_BASENAME', plugin_basename( __FILE__ ) );
define( 'SE_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
define( 'SE_PLUGIN_URL', untrailingslashit( plugin_dir_url( __FILE__ ) ) );
Expand Down
8 changes: 8 additions & 0 deletions src/blocks/loop-event-info/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@
"order": {
"type": "string",
"default": "asc"
},
"dateFormat": {
"type": "string",
"default": ""
},
"timeFormat": {
"type": "string",
"default": ""
}
},
"supports": {
Expand Down
72 changes: 70 additions & 2 deletions src/blocks/loop-event-info/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import './index.scss';
import './editor.scss';
import metadata from './block.json';

import { __ } from '@wordpress/i18n';
import { __, sprintf } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import {
PanelBody,
Expand All @@ -19,9 +19,27 @@ import {
} from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
import { dateI18n, getSettings as getDateSettings } from '@wordpress/date';

registerBlockType(metadata, {
edit: ({ attributes: { metaName, metaPrefix, thePostId, textAlign, addCalendarLinks, feedType, order }, setAttributes, context: { postId }, clientId }) => {
edit: ({ attributes: { metaName, metaPrefix, thePostId, textAlign, addCalendarLinks, feedType, order, dateFormat, timeFormat }, setAttributes, context: { postId }, clientId }) => {

const siteFormats = getDateSettings().formats;
const siteDateFormat = siteFormats.date;
const siteTimeFormat = siteFormats.time;
const showDateFormat = metaName === 'dates' || metaName === 'date';
const showTimeFormat = metaName === 'dates' || metaName === 'time';

const formatPreview = (format) => {
if (!format) {
return '';
}
try {
return dateI18n(format, new Date());
} catch (e) {
return '';
}
};

// Get query loop data from our custom store
const queryData = useSelect((select) => {
Expand Down Expand Up @@ -87,6 +105,54 @@ registerBlockType(metadata, {
setAttributes({ addCalendarLinks: value } )
}
/>
{ showDateFormat && (
<TextControl
label={__('Date format override', 'simple-events')}
help={
dateFormat
? sprintf(
/* translators: %s: rendered date example. */
__('Preview: %s', 'simple-events'),
formatPreview(dateFormat)
)
: sprintf(
/* translators: %s: site default date format. */
__('Leave empty to use the site default (%s).', 'simple-events'),
siteDateFormat
)
}
placeholder={siteDateFormat}
value={dateFormat}
onChange={(value) =>
setAttributes({ dateFormat: value })
}
__nextHasNoMarginBottom
/>
) }
{ showTimeFormat && (
<TextControl
label={__('Time format override', 'simple-events')}
help={
timeFormat
? sprintf(
/* translators: %s: rendered time example. */
__('Preview: %s', 'simple-events'),
formatPreview(timeFormat)
)
: sprintf(
/* translators: %s: site default time format. */
__('Leave empty to use the site default (%s).', 'simple-events'),
siteTimeFormat
)
}
placeholder={siteTimeFormat}
value={timeFormat}
onChange={(value) =>
setAttributes({ timeFormat: value })
}
__nextHasNoMarginBottom
/>
) }
</PanelBody>
</InspectorControls>
<BlockControls group="block">
Expand All @@ -108,6 +174,8 @@ registerBlockType(metadata, {
addCalendarLinks,
feedType, // Use block attribute values
order, // Use block attribute values
dateFormat,
timeFormat,
}}
/>
</div>
Expand Down
42 changes: 40 additions & 2 deletions src/classes/class-date-display-formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ class SE_Date_Display_Formatter {
*/
private $use_html_in_date_output = false;

/**
* Override for the date format. Empty string means use the site option.
*
* @var string
*/
private $date_format = '';

/**
* Override for the time format. Empty string means use the site option.
*
* @var string
*/
private $time_format = '';

/**
* Create a new instance of the date display formatter.
*
Expand Down Expand Up @@ -169,6 +183,28 @@ public function set_time_only( bool $time_only = true ) {
$this->time_only = $time_only;
}

/**
* Override the date format used by format_date(). Empty string restores the site default.
*
* @param string $format A PHP date format string.
*
* @return void
*/
public function set_date_format( string $format ): void {
$this->date_format = $format;
}

/**
* Override the time format used by format_time(). Empty string restores the site default.
*
* @param string $format A PHP date format string.
*
* @return void
*/
public function set_time_format( string $format ): void {
$this->time_format = $format;
}
Comment on lines +193 to +206
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sanitize format overrides before storing to prevent HTML injection in rendered date output.

Line 193 and Line 204 accept raw strings that later flow into frontend-rendered date/time text. A format like <script>... is treated as literal output by date formatting and can be rendered unescaped.

Suggested fix
 public function set_date_format( string $format ): void {
-	$this->date_format = $format;
+	$this->date_format = trim( wp_strip_all_tags( $format ) );
 }
@@
 public function set_time_format( string $format ): void {
-	$this->time_format = $format;
+	$this->time_format = trim( wp_strip_all_tags( $format ) );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/classes/class-date-display-formatter.php` around lines 193 - 206, The
setters set_date_format and set_time_format currently store raw user-provided
strings which can include HTML/JS and later be rendered; update both methods
(set_date_format and set_time_format) to sanitize the incoming $format before
assigning to $this->date_format and $this->time_format (e.g., use a
WordPress-safe sanitizer like sanitize_text_field() or wp_strip_all_tags() to
strip tags/unsafe characters and optionally validate against a whitelist/regex
of allowed date format characters), so only cleaned format strings are stored
and later rendered.


/**
* Modify Timezone.
*
Expand Down Expand Up @@ -630,7 +666,8 @@ private function get_timezone_abbreviation() {
* @return string
*/
public function format_date( $date_timestamp ) {
return wp_date( get_option( 'date_format' ), $date_timestamp, $this->get_timezone_instance() );
$format = '' !== $this->date_format ? $this->date_format : get_option( 'date_format' );
return wp_date( $format, $date_timestamp, $this->get_timezone_instance() );
}

/**
Expand All @@ -641,7 +678,8 @@ public function format_date( $date_timestamp ) {
* @return string
*/
public function format_time( $time_timestamp ) {
return wp_date( get_option( 'time_format' ), $time_timestamp, $this->get_timezone_instance() );
$format = '' !== $this->time_format ? $this->time_format : get_option( 'time_format' );
return wp_date( $format, $time_timestamp, $this->get_timezone_instance() );
}

/**
Expand Down
9 changes: 6 additions & 3 deletions src/classes/class-se-blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,9 @@ public static function loop_event_info_render( $attributes, $content, $block ):
break;
}

$date_format = isset( $attributes['dateFormat'] ) ? (string) $attributes['dateFormat'] : '';
$time_format = isset( $attributes['timeFormat'] ) ? (string) $attributes['timeFormat'] : '';

// Generate output based on meta name.
if ( ! empty( $post_ID ) ) {
switch ( $attributes['metaName'] ) {
Expand All @@ -852,13 +855,13 @@ public static function loop_event_info_render( $attributes, $content, $block ):
$output = se_event_get_venue( $post_ID );
break;
case 'dates':
$output = $get_date_function( $post_ID, $event_date_id );
$output = $get_date_function( $post_ID, $event_date_id, false, false, null, $date_format, $time_format );
break;
case 'date':
$output = $get_date_function( $post_ID, $event_date_id, true, false );
$output = $get_date_function( $post_ID, $event_date_id, true, false, null, $date_format, $time_format );
break;
case 'time':
$output = $get_date_function( $post_ID, $event_date_id, false, true );
$output = $get_date_function( $post_ID, $event_date_id, false, true, null, $date_format, $time_format );
break;
}
}
Expand Down
33 changes: 30 additions & 3 deletions src/event-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ function ( $date ) {
* @param boolean $date_only Whether to return only the date.
* @param boolean $time_only Whether to return only the time.
* @param array $event_dates Event dates.
* @param string $date_format Optional date format override. Empty string uses the site default.
* @param string $time_format Optional time format override. Empty string uses the site default.
*
* @return string
*/
function se_event_get_future_dates( $event_id, $event_date_id = null, $date_only = false, $time_only = false, $event_dates = null ) {
function se_event_get_future_dates( $event_id, $event_date_id = null, $date_only = false, $time_only = false, $event_dates = null, $date_format = '', $time_format = '' ) {
$date_display_formatter = new SE_Date_Display_Formatter( $event_id );
$now = SE_Calendar::get_instance()->create_date_time( 'now' )->format( 'U' );

Expand All @@ -173,6 +175,13 @@ function se_event_get_future_dates( $event_id, $event_date_id = null, $date_only
$date_display_formatter->set_time_only( true );
}

if ( '' !== $date_format ) {
$date_display_formatter->set_date_format( $date_format );
}
if ( '' !== $time_format ) {
$date_display_formatter->set_time_format( $time_format );
}

// If we dont have any dates.
if ( ! $event_dates ) {
$event_dates = se_event_get_event_dates( $event_id );
Expand Down Expand Up @@ -214,10 +223,12 @@ function ( $date ) use ( $now ) {
* @param boolean $date_only Whether to return only the date.
* @param boolean $time_only Whether to return only the time.
* @param array $event_dates Event dates.
* @param string $date_format Optional date format override. Empty string uses the site default.
* @param string $time_format Optional time format override. Empty string uses the site default.
*
* @return string
*/
function se_event_get_past_dates( $event_id, $event_date_id = null, $date_only = false, $time_only = false, $event_dates = null ) {
function se_event_get_past_dates( $event_id, $event_date_id = null, $date_only = false, $time_only = false, $event_dates = null, $date_format = '', $time_format = '' ) {

// Match the se_event_get_future_dates but for past dates
$date_display_formatter = new SE_Date_Display_Formatter( $event_id );
Expand All @@ -229,6 +240,13 @@ function se_event_get_past_dates( $event_id, $event_date_id = null, $date_only =
$date_display_formatter->set_time_only( true );
}

if ( '' !== $date_format ) {
$date_display_formatter->set_date_format( $date_format );
}
if ( '' !== $time_format ) {
$date_display_formatter->set_time_format( $time_format );
}

// If we dont have any dates.
if ( ! $event_dates ) {
$event_dates = se_event_get_event_dates( $event_id );
Expand Down Expand Up @@ -268,10 +286,12 @@ function ( $date ) use ( $now ) {
* @param boolean $date_only Whether to return only the date.
* @param boolean $time_only Whether to return only the time.
* @param array $event_dates Event dates.
* @param string $date_format Optional date format override. Empty string uses the site default.
* @param string $time_format Optional time format override. Empty string uses the site default.
*
* @return string
*/
function se_event_get_formatted_dates( $event_id, $event_date_id = null, $date_only = false, $time_only = false, $event_dates = null ) {
function se_event_get_formatted_dates( $event_id, $event_date_id = null, $date_only = false, $time_only = false, $event_dates = null, $date_format = '', $time_format = '' ) {

$date_display_formatter = new SE_Date_Display_Formatter( $event_id );

Expand Down Expand Up @@ -299,6 +319,13 @@ function ( $date ) use ( $event_date_id ) {
$date_display_formatter->set_time_only( true );
}

if ( '' !== $date_format ) {
$date_display_formatter->set_date_format( $date_format );
}
if ( '' !== $time_format ) {
$date_display_formatter->set_time_format( $time_format );
}

return $date_display_formatter->format_dates( $event_dates );
}

Expand Down
26 changes: 26 additions & 0 deletions src/variations/query-loop-events/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ const FeedTypeControl = ({ attributes, setAttributes, clientId }) => {
const [eventsPerPage, setEventsPerPage] = useState(
query.perPage || 6
);
const [eventsOffset, setEventsOffset] = useState(
query.offset || 0
);

// Store the query data so child blocks can access it
useEffect(() => {
Expand Down Expand Up @@ -184,6 +187,29 @@ let feedOrderOptions = getFeedOrderOptions(feedType);
step={1}
__nextHasNoMarginBottom
/>
<RangeControl
label={__('Offset', 'simple-events')}
help={__(
'Number of events to skip from the start of the query.',
'simple-events'
)}
value={eventsOffset}
onChange={(value) => {
const nextOffset = value || 0;
setEventsOffset(nextOffset);
setAttributes({
query: {
...query,
offset: nextOffset,
_cacheBuster: Date.now()
},
});
}}
min={0}
max={100}
step={1}
__nextHasNoMarginBottom
/>
<p className="description">
{__(
'Select the type of events to display and their order.',
Expand Down
Loading