Skip to content

Commit cb34f80

Browse files
committed
Merge branch 'develop' of https://github.com/rpgmem/ffcertificate into develop
2 parents 2b65eaf + c3ca6df commit cb34f80

5 files changed

Lines changed: 105 additions & 21 deletions

File tree

‎assets/js/ffc-geofence-validation.js‎

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@
4949
// direct caller benefit from the same default the server uses.
5050
var timeMode = v.time_mode || 'daily';
5151

52+
// Event Schedule (Reference) — `class_time_*` is independent of
53+
// the date/time-restriction inputs, so evaluate it FIRST. The
54+
// early returns inside the date-order / span-mode short-circuits
55+
// below would otherwise prevent it from ever firing in span mode
56+
// (the bug the live editor exposed — inverted Event Schedule
57+
// alongside a valid Date/Time Restrictions span only flagged the
58+
// latter).
59+
if (v.class_time_start && v.class_time_end && v.class_time_end <= v.class_time_start) {
60+
var classMsg = msg('class_time_order');
61+
errors.class_time_start = classMsg;
62+
errors.class_time_end = classMsg;
63+
}
64+
5265
if (v.date_start && v.date_end && v.date_end < v.date_start) {
5366
var dateMsg = msg('date_order');
5467
errors.date_start = dateMsg;
@@ -75,15 +88,6 @@
7588
errors.time_end = dailyMsg;
7689
}
7790

78-
// Event Schedule (Reference) — mirrors the PHP rule for
79-
// `class_time_*` so the live red-border feedback fires on those
80-
// inputs too.
81-
if (v.class_time_start && v.class_time_end && v.class_time_end <= v.class_time_start) {
82-
var classMsg = msg('class_time_order');
83-
errors.class_time_start = classMsg;
84-
errors.class_time_end = classMsg;
85-
}
86-
8791
return errors;
8892
}
8993

‎assets/js/ffc-geofence-validation.min.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎includes/security/class-ffc-geofence.php‎

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,23 @@ public static function analyze_datetime_order( array $config ): array {
7474
$time_end = isset( $config['time_end'] ) ? (string) $config['time_end'] : '';
7575
$time_mode = isset( $config['time_mode'] ) ? (string) $config['time_mode'] : 'daily';
7676

77+
// Event Schedule (Reference) — `class_time_*` drives the
78+
// `{{schedule}}` placeholder and behaves like a single-day daily
79+
// range, so end must come after start when both are filled.
80+
// Evaluated FIRST so it always runs: the early returns below for
81+
// the date-order / span-mode short-circuits operate on different
82+
// inputs and would otherwise prevent the class_time check from
83+
// firing in span mode (the scenario the live editor exposed —
84+
// the operator inverts Event Schedule alongside a valid Date/Time
85+
// Restrictions span and only the latter gets red borders).
86+
$class_time_start = isset( $config['class_time_start'] ) ? (string) $config['class_time_start'] : '';
87+
$class_time_end = isset( $config['class_time_end'] ) ? (string) $config['class_time_end'] : '';
88+
if ( '' !== $class_time_start && '' !== $class_time_end && $class_time_end <= $class_time_start ) {
89+
$msg = __( 'Event Schedule end time must be later than the start time.', 'ffcertificate' );
90+
$errors['class_time_start'] = $msg;
91+
$errors['class_time_end'] = $msg;
92+
}
93+
7794
// Date order — applies in any time_mode.
7895
if ( '' !== $date_start && '' !== $date_end && $date_end < $date_start ) {
7996
$msg = __( 'End date is earlier than the start date.', 'ffcertificate' );
@@ -112,17 +129,6 @@ public static function analyze_datetime_order( array $config ): array {
112129
$errors['time_end'] = $msg;
113130
}
114131

115-
// Event Schedule (Reference) — `class_time_*` drives the
116-
// `{{schedule}}` placeholder and behaves like a single-day daily
117-
// range, so end must come after start when both are filled.
118-
$class_time_start = isset( $config['class_time_start'] ) ? (string) $config['class_time_start'] : '';
119-
$class_time_end = isset( $config['class_time_end'] ) ? (string) $config['class_time_end'] : '';
120-
if ( '' !== $class_time_start && '' !== $class_time_end && $class_time_end <= $class_time_start ) {
121-
$msg = __( 'Event Schedule end time must be later than the start time.', 'ffcertificate' );
122-
$errors['class_time_start'] = $msg;
123-
$errors['class_time_end'] = $msg;
124-
}
125-
126132
return $errors;
127133
}
128134

‎tests/Unit/GeofenceTest.php‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,4 +423,45 @@ public function test_analyze_datetime_order_class_time_skips_when_only_one_fille
423423
$errors_only_end = Geofence::analyze_datetime_order( array( 'class_time_end' => '17:30' ) );
424424
$this->assertSame( array(), $errors_only_end );
425425
}
426+
427+
public function test_analyze_datetime_order_flags_class_time_alongside_span_mode_inversion(): void {
428+
// Regression for the user-reported bug: inverted Event Schedule
429+
// alongside an inverted span-mode Date/Time Restrictions only
430+
// flagged the latter, because the span branch early-returned
431+
// before the class_time check at the tail of the function ran.
432+
// Both pairs of errors must surface so both sets of inputs go red.
433+
$config = array(
434+
'date_start' => '2026-05-24',
435+
'date_end' => '2026-05-24',
436+
'time_start' => '21:00',
437+
'time_end' => '20:00',
438+
'time_mode' => 'span',
439+
'class_time_start' => '21:00',
440+
'class_time_end' => '20:00',
441+
);
442+
$errors = Geofence::analyze_datetime_order( $config );
443+
// span check picks up time_*.
444+
$this->assertArrayHasKey( 'time_start', $errors );
445+
$this->assertArrayHasKey( 'time_end', $errors );
446+
// class_time check picks up class_time_* (this was the missing
447+
// half before the fix that moved it ahead of the early returns).
448+
$this->assertArrayHasKey( 'class_time_start', $errors );
449+
$this->assertArrayHasKey( 'class_time_end', $errors );
450+
}
451+
452+
public function test_analyze_datetime_order_flags_class_time_alongside_date_inversion(): void {
453+
// Same regression as above for the date-order short-circuit
454+
// (which returns earlier than the span branch).
455+
$config = array(
456+
'date_start' => '2026-06-30',
457+
'date_end' => '2026-06-01',
458+
'class_time_start' => '14:00',
459+
'class_time_end' => '12:00',
460+
);
461+
$errors = Geofence::analyze_datetime_order( $config );
462+
$this->assertArrayHasKey( 'date_start', $errors );
463+
$this->assertArrayHasKey( 'date_end', $errors );
464+
$this->assertArrayHasKey( 'class_time_start', $errors );
465+
$this->assertArrayHasKey( 'class_time_end', $errors );
466+
}
426467
}

‎tests/js/geofence-admin.test.js‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,37 @@ describe('FFCGeofenceAdmin.analyzeDateTimeOrder', () => {
134134
expect(analyze({ class_time_start: '09:00' })).toEqual({});
135135
expect(analyze({ class_time_end: '17:30' })).toEqual({});
136136
});
137+
138+
it('flags Event Schedule alongside a span-mode inversion (regression)', () => {
139+
// User-reported bug: in span mode the function early-returned
140+
// before the class_time check at the tail ran, so the Event
141+
// Schedule inputs never went red even with an inverted range.
142+
const errors = analyze({
143+
date_start: '2026-05-24',
144+
date_end: '2026-05-24',
145+
time_start: '21:00',
146+
time_end: '20:00',
147+
time_mode: 'span',
148+
class_time_start: '21:00',
149+
class_time_end: '20:00',
150+
});
151+
// Both pairs must be flagged.
152+
expect(errors.time_start).toBeTruthy();
153+
expect(errors.time_end).toBeTruthy();
154+
expect(errors.class_time_start).toBeTruthy();
155+
expect(errors.class_time_end).toBeTruthy();
156+
});
157+
158+
it('flags Event Schedule alongside a date-order inversion (regression)', () => {
159+
const errors = analyze({
160+
date_start: '2026-06-30',
161+
date_end: '2026-06-01',
162+
class_time_start: '14:00',
163+
class_time_end: '12:00',
164+
});
165+
expect(errors.date_start).toBeTruthy();
166+
expect(errors.date_end).toBeTruthy();
167+
expect(errors.class_time_start).toBeTruthy();
168+
expect(errors.class_time_end).toBeTruthy();
169+
});
137170
});

0 commit comments

Comments
 (0)