Browser/Plugin improvements: counting nodes, ordering and Nodes section at top/bottom#1575
Browser/Plugin improvements: counting nodes, ordering and Nodes section at top/bottom#1575rNoz wants to merge 9 commits into
Conversation
…ring resources without lastmodified (found in manual UI tests)
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1575 +/- ##
=========================================
Coverage 97.23% 97.23%
- Complexity 2836 2843 +7
=========================================
Files 175 175
Lines 9028 9043 +15
=========================================
+ Hits 8778 8793 +15
Misses 250 250 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@rNoz php-cs-fixer has some minor formatting required. https://github.com/sabre-io/dav/actions/runs/12096265416/job/34109861744?pr=1575 You can run it locally |
| // If there are nodes and they are more than the max number to show at the top of the page | ||
| if ($numSubNodes) { | ||
| $html .= $this->generateNodesSection($subNodes, $numSubNodes); |
There was a problem hiding this comment.
Can you add a test case for when there are more than 20 nodes?
That will exercise this code, and the test can check that the Nodes section comes down the bottom of the HTML.
There was a problem hiding this comment.
Sure. Created testCollectionWithManyNodesGetSubdir. I have also updated the normal case (testCollectionGetRoot) to check that is at the top.
|
@phil-davis I needed to update the |
|
@DeepDiver1975 @staabm can either (or both) review this? |
staabm
left a comment
There was a problem hiding this comment.
I have no strong opinion on such changes.
looks useful, but I can't judge how this plugin is used in the wild.
I won't have time to look into more details for a few days
DeepDiver1975
left a comment
There was a problem hiding this comment.
Code Review
Refactors generateDirectoryIndex() to extract node-table rendering into generateNodesSection(), add a node count to the heading, place the Nodes section at the top (≤20 children) or bottom (>20), and sort by last-modified descending then natural name order. Nicely done overall — clear extraction and good test coverage.
Strengths
- The extraction of
generateNodesSection()is clean and removes the awkward inline block. - It quietly fixes a latent bug: the original code appended
$html .= '</section>';outside theif ($node instanceof DAV\ICollection)block, emitting a stray closing</section>for non-collection nodes (and an empty<section><h1>Nodes</h1>for empty collections). The new code only emits the section when there are nodes. - Comparator change is null-safe and uses the spaceship operator correctly for descending order.
- Good use of XPath to assert section placement rather than just substring presence.
Suggestions
- Behavior change — default sort order. Sorting by last-modified-desc instead of alphabetically is a visible change for every consumer of the Browser plugin, not just large collections. Defensible UX-wise, but it's a silent default change — worth a changelog note, and ideally opt-in/configurable since some integrators may rely on alphabetical ordering.
- Magic number
20.$maxNodesAtTopSection = 20;is a local variable. Since the plugin is subclassable and this is a tunable UX knob, aprotectedclass property/constant would let subclasses override it without copying the whole method. It's also duplicated intestCollectionWithManyNodesGetSubdir($maxNodes = 20) — promoting it would keep test and code in sync. - Redundant
$numSubNodesparameter.generateNodesSection($subNodes, $numSubNodes)could derive the count internally withcount($subNodes); passing it in invites the two getting out of sync. $numSubNodes = 0;as a control flag (reset after rendering at the top to suppress the bottom render) works but reads as a side-effect. A separate boolean like$nodesRenderedwould be more self-documenting.
Tests
testCollectionNodesOrderasserts the comparator returns exactly-1/1. The last-modified branch uses<=>(always ±1/0, fine), but thestrnatcasecmp()fallback is not contractually guaranteed to return exactly ±1 — it's normalized on modern PHP, so it passes today, but asserting the sign (assertLessThan(0, ...)/assertGreaterThan(0, ...)) for the name-ordering cases would be more robust.- Typo:
$file1_clon→$file1_clone(cosmetic).
Security & performance
- No new injection surface: dynamic output still routes through
escapeHTML(), the count is an integer, CSP header unchanged. Performance is equivalent to before.
Verdict: Solid, well-tested improvement that also fixes a pre-existing stray-</section> bug. Main thing to confirm before merge is the silent change to the default sort order (alphabetical → last-modified desc) — is that the intended default, or should it be opt-in?
🤖 Generated with Claude Code
What am I doing?
Providing some tweaks to
Sabre\DAV\Browser\Plugin.php:Example with this PR:
Why?
More context: issue
I have added a test to evaluate the new
compareNodesprotected function, as I didn't see a way to easily replacelastmodifiedvalues for a set of files and just parsing the table rows of the response body. So I am using reflection to verify all the possibilities when comparing.Originally, I had another function (see below) to evaluate specifically the counting of nodes, but I think it will be fine just using what is provided by the
parent::setUp()+setUp(3 nodes).All passing:
Let me know if something else is necessary ;)