Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 16 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,19 @@ Update the changelog whenever you:
Also update the version number in:
- `mayo-events-manager.php` (line 23: `MAYO_VERSION` constant)
- `readme.txt` (line 8: `Stable tag`)
- `package.json` (line 3: `version`)
- `package.json` (line 3: `version`)

## Encoding Guidelines for REST API Data

This codebase has had multiple encoding bugs (#41, #84, #109, #222, #249). Follow these rules when returning data via REST API:

### URLs
- **REST API/JSON context**: Use `esc_url_raw($url)` - preserves `&` characters
- **HTML output context**: Use `esc_url($url)` - encodes `&` to `&`

### Text/Titles
- **REST API/JSON context**: Use `html_entity_decode($text, ENT_QUOTES, 'UTF-8')`
- **HTML output context**: Use `esc_html($text)` or `esc_attr($text)`

### Why?
WordPress escaping functions encode special characters for HTML safety. When this data goes through REST API → JSON → React, the HTML entities cause display issues or broken functionality.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"test": "phpunit",
"test:unit": "phpunit --testsuite Unit",
"lint": "phpcs *.php",
"lint:fix": "phpcbf *.php"
"lint:fix": "phpcbf *.php",
"lint:esc-url": "bash scripts/check-esc-url.sh",
"lint:all": ["@lint", "@lint:esc-url"]
},
"config": {
"allow-plugins": {
Expand Down
2 changes: 1 addition & 1 deletion includes/Announcement.php
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ public static function resolve_event_ref($ref) {
return [
'id' => $ref['id'] ?? 0,
'title' => sanitize_text_field($ref['title']),
'permalink' => esc_url($ref['url']),
'permalink' => esc_url_raw($ref['url']),
'icon' => isset($ref['icon']) ? sanitize_text_field($ref['icon']) : 'external',
'source' => [
'type' => 'custom',
Expand Down
4 changes: 2 additions & 2 deletions mayo-events-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* Plugin Name: Mayo Events Manager
* Description: A plugin for managing and displaying events.
* Version: 1.8.5
* Version: 1.8.6
* Author: bmlt-enabled
* License: GPLv2 or later
* Author URI: https://bmlt.app
Expand All @@ -20,7 +20,7 @@
exit; // Exit if accessed directly
}

define('MAYO_VERSION', '1.8.5');
define('MAYO_VERSION', '1.8.6');

require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/includes/Admin.php';
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mayo",
"version": "1.8.5",
"version": "1.8.6",
"description": "",
"main": "index.js",
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Tags: events, bmlt, narcotics anonymous, na
Requires PHP: 8.2
Requires at least: 6.7
Tested up to: 6.9
Stable tag: 1.8.5
Stable tag: 1.8.6
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html

Expand Down Expand Up @@ -187,6 +187,9 @@ This project is licensed under the GPL v2 or later.

== Changelog ==

= 1.8.6 =
* Fixed custom URLs in announcements having ampersands incorrectly HTML-encoded, causing links with query parameters to fail. [#249]

= 1.8.5 =
* Fixed external feed events not displaying service body names correctly. [#234]
* Fixed events and announcements remaining visible after their scheduled end time passes. Previously only the end date was checked, ignoring the end time. [#237]
Expand Down
23 changes: 23 additions & 0 deletions scripts/check-esc-url.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
# Check for esc_url() usage in REST API contexts (should use esc_url_raw() instead)
# See issue #249 for context

ERRORS=0

# Check in includes/Rest directory
if grep -rn "esc_url(" includes/Rest/ 2>/dev/null; then
echo "WARNING: Found esc_url() in includes/Rest/ - use esc_url_raw() for REST API contexts"
ERRORS=1
fi

# Check in Announcement.php (has REST-consumed methods)
if grep -n "esc_url(" includes/Announcement.php 2>/dev/null | grep -v "esc_url_raw"; then
echo "WARNING: Found esc_url() in includes/Announcement.php - verify it's not used for REST API data"
ERRORS=1
fi

if [ $ERRORS -eq 0 ]; then
echo "No esc_url() issues found in REST contexts"
fi

exit $ERRORS
27 changes: 27 additions & 0 deletions tests/Unit/AnnouncementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,33 @@ public function testResolveEventRefHandlesCustomLinks(): void {
$this->assertEquals('custom', $result['source']['type']);
}

/**
* Test resolve_event_ref preserves URL query parameters (issue #249)
*
* Custom URLs with ampersands in query strings must not be HTML-encoded
* when returned via REST API.
*/
public function testResolveEventRefPreservesUrlQueryParameters(): void {
$ref = [
'type' => 'custom',
'url' => 'https://example.com/link?id=123&key=GRP&app=resvlink',
'title' => 'Book Room',
'icon' => 'external'
];

$result = Announcement::resolve_event_ref($ref);

$this->assertNotNull($result);
// URL should preserve literal & characters, not HTML entities
$this->assertEquals(
'https://example.com/link?id=123&key=GRP&app=resvlink',
$result['permalink']
);
// Explicitly verify no HTML encoding
$this->assertStringNotContainsString('&', $result['permalink']);
$this->assertStringNotContainsString('&', $result['permalink']);
}

/**
* Test resolve_event_ref returns null for custom link without url
*/
Expand Down