Tags: dereuromark/cakephp-feed
Tags
Add AtomView for Atom 1.0 feed generation (#26) * Add AtomView for generating Atom 1.0 feeds Atom 1.0 (RFC 4287) is the cleaner, better-specified counterpart to RSS 2.0. This view sits alongside RssView and uses the same SerializedView shape: $this->set(['feed' => $feed]); $this->viewBuilder()->setOption('serialize', 'feed'); $this->viewBuilder()->setClassName('Feed.Atom'); What the AtomView class does - Default xmlns binding for the Atom namespace, plus opt-in xmlns:prefix declarations for dc / content / itunes (and any custom prefix the caller registers via setNamespace() or a top-level 'namespace' key). Unused declarations stay out of the output. - Friendly shorthands for the common case alongside the full attribute shape. Bare strings work for 'link' (defaults to rel=alternate), 'author' (becomes <author><name>...</name></author>), and 'category' (becomes <category term="..."/>). Pass an array with @-prefixed keys when you need multiple links per entry, structured authors, or category schemes. - RFC 3339 dates. The time() helper accepts DateTimeInterface, an int unix timestamp, or any string DateTime can parse, and normalizes to the RFC 3339 form Atom requires. Separate 'published' / 'updated' fields per entry are preserved verbatim — this is one of Atom's main wins over RSS. - Typed content. 'content' / 'summary' / 'rights' / 'subtitle' / 'title' accept the full text-construct shape (@type and @ body). HTML content is CDATA-wrapped via the same placeholder pattern RssView uses, so reader markup survives the XML encode without double-escaping. Plain text is left un-CDATA'd so the output stays clean. - Loud failure on unregistered namespaces. A prefix:tag key whose prefix was never registered throws RuntimeException (wrapped in Cake's SerializationFailureException) rather than emitting a feed with a broken namespace decl. - 'atom' shorthand registered for MIME / route extension. Both the plugin bootstrap and the AtomView constructor call MimeType::setMimeTypes('atom', 'application/atom+xml') so $response-> withType('atom') resolves and Router::extensions(['atom']) maps .atom URLs to this view. Cleanup - Restored the 'atom' composer keyword (previously stripped in the bug PR because the capability didn't exist). - README now describes both view classes plus a "which one to use" section pointing podcast publishers at RSS and most other use cases at Atom. - Removed the "Add AtomView ?" TODO entry from the README. - docs/README.md and a new docs/View/Atom.md cover field-by-field input shape, shorthands, and worked examples. Testing 13 new tests under tests/TestCase/View/AtomViewTest.php covering: - Date normalization across DateTimeImmutable / int / string inputs - Minimal feed shape (id/title/updated + default namespace binding) - link shorthand vs explicit @href arrays vs list of links - author shorthand-vs-structured equivalence - Multiple <author> and <contributor> per feed (Atom-only feature) - HTML content gets CDATA-wrapped; text content does not - category shorthand + full attribute form including @scheme/@Label - 'published' and 'updated' as distinct fields on the same entry - Namespace decls only emitted for prefixes actually used - Unregistered prefix throws via RuntimeException unwrapped from SerializationFailureException - Top-level 'namespace' key registers custom prefixes - Content-Type advertised matches the Atom MIME spec - Constructor wires withType('atom') on the response phpunit (32/32), phpstan, phpcs all clean. * Use Response::setTypeMap instead of Http\MimeType (5.1 compat) Cake\Http\MimeType only landed in CakePHP 5.2. The plugin requires ^5.1.1, so prefer-lowest CI installs 5.1.x where the class doesn't exist — every AtomView test failed with 'Class Cake\Http\MimeType not found' on that matrix entry. Switch to the older Response::setTypeMap() instance method. It's available on every 5.x version and the underlying mime-type map lives in static state, so calling it on a throwaway response instance is functionally identical to the static MimeType call. Verified locally with prefer-lowest: 32/32 still passes. * Enforce Atom required fields and reject XHTML text constructs * Address Copilot review: Cake URL array shorthand, CDATA terminator split, doc fixes Three Copilot review concerns on top of 88d8531 (required-field enforcement and XHTML rejection): 1. `_oneLink()` only normalized strings and `@href` arrays. Anyone passing a Cake URL array (`['controller' => 'Posts', 'action' => 'view', 1]`) — supported by RssView and documented there — got the raw array serialized as nested XML children of `<link>` instead of a proper `<link href="/sitelet?url=https%3A%2F%2Fgithub.com%2Fdereuromark%2Fcakephp-feed%2F..."/>` attribute element. The new branch detects associative arrays without `@href` and routes them through `Router::url(/sitelet?url=https%3A%2F%2Fgithub.com%2Fdereuromark%2Fcakephp-feed%2F...%2C%2520true)`, matching RssView semantics. 2. CDATA wrapping was naive: a literal `]]>` inside the HTML body would close the CDATA section early and produce malformed XML. Replaced with the canonical split `]]]]><![CDATA[>`, which round-trips to the original `]]>` on parse but never terminates the outer CDATA prematurely. Two regression tests confirm: one direct string assertion on the emitted output, and one that re-parses the entire feed via `simplexml_load_string()` to prove well-formedness and verify the round-tripped text matches the input. 3. The class-level docblock claimed plain-string `content` was a shorthand for `type="html"` with CDATA wrapping. The actual `_prepareText()` returns strings as-is (they end up as `type="text"` and XML-escaped). Aligned the docblock with the implementation: plain strings are always `type="text"`, HTML needs the explicit `@type` + `@` array form. Also rewrote the input-shape paragraph to acknowledge the now-enforced required fields from 88d8531. phpunit (37/37), phpstan, phpcs all clean.
Skip empty enclosures and use array_is_list for category lists (#24) Two correctness fixes in src/View/RssView.php with regression tests. - src/View/RssView.php:323: enclosure branch now returns early when url is missing, empty, or non-string instead of emitting an invalid <enclosure url=""/> element. - src/View/RssView.php:272: category list arm uses array_is_list() so lists whose first element is falsy (0, '', null, false) still render as multiple <category> elements rather than collapsing into one. - tests/TestCase/View/RssViewTest.php: add regression tests for both.
Fix critical security vulnerability and code quality issues (#21) * Fix critical security vulnerability and code quality issues Security Fixes: - Fix path traversal vulnerability in enclosure file handling - Add realpath validation to ensure files stay within WWW_ROOT - Replace deprecated mime_content_type() with finfo_file() Critical Bug Fixes: - Fix category handling logic bug (wrong variable used in foreach) - Add null coalescing operators for safe array access - Fix array initialization ($attrib from null to empty array) Type Safety Improvements: - Add type hint to time() parameter (DateTime|string|int) - Add type declaration to $version property (string) - Improve subDir property documentation with deprecation details Code Quality: - Remove redundant file_exists() check (covered by is_file) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix CS --------- Co-authored-by: Claude <noreply@anthropic.com>
PreviousNext