GravityView icon indicating copy to clipboard operation
GravityView copied to clipboard

Add AJAX endpoint for immediate entry lock release on navigation away

Open omarkasem opened this issue 4 months ago • 5 comments

This update introduces a new AJAX action to release entry locks when users navigate away from the edit page. It includes a JavaScript handler that sends a beacon request to the server, ensuring that locks are properly released, enhancing user experience and preventing lock conflicts.

Summary by CodeRabbit

  • New Features

    • Adds per-entry lock timestamps and heartbeat interval tracking so locks refresh reliably and expire when stale.
    • UI now sends the heartbeatInterval with each heartbeat to keep lock state in sync.
  • Bug Fixes

    • Automatically clears stale locks and pending requests so old locks no longer block edits.
    • Heartbeat flow updates lock activity and intervals to reduce false lock conflicts.

💾 Build file (e9f6f67ba).

omarkasem avatar Sep 28 '25 15:09 omarkasem

[!CAUTION]

Review failed

The pull request is closed.

Walkthrough

Adds timestamp and interval tracking for entry locks, plus staleness detection and handling. Lock acquisition/refresh flows update timestamps/intervals; stale locks are cleared and immediately granted, emitting a stale-grant action. Heartbeat integration propagates interval and updates metadata.

Changes

Cohort / File(s) Summary
Edit Entry Locking: timestamp, interval & stale-lock handling
includes/extensions/edit-entry/class-edit-entry-locking.php
Adds constants LOCK_TIMESTAMP_PREFIX, LOCK_INTERVAL_PREFIX, STALE_THRESHOLD_MULTIPLIER. Adds protected methods update_lock_timestamp(), get_lock_timestamp(), update_lock_interval(), get_lock_interval(), get_lock_stale_threshold(), is_lock_stale(). delete_lock_meta() now clears timestamp and interval caches. set_lock() and heartbeat_refresh_lock() update timestamps (and interval when provided). heartbeat_request_lock() detects stale locks, clears old lock/meta and pending requests, grants stale locks immediately and fires lock_granted_stale; otherwise preserves existing pending/active lock flow. Minor formatting/whitespace adjustments in inline HTML/JS.
Heartbeat / UI integration (inline script & enqueue)
includes/extensions/edit-entry/class-edit-entry-locking.php
When a logged-in user without the lock loads UI, current heartbeatInterval is stored via update_lock_interval(). Heartbeat handling uses strict int casting; inline script updated to pass heartbeatInterval and adjusted formatting only. Public AJAX signatures unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Browser as Client (heartbeat/request)
  participant Server as Lock Manager
  participant Store as Lock Meta Store

  User->>Browser: open edit UI / send heartbeat
  Browser->>Server: heartbeat_request_lock(entry_id, user_id, heartbeatInterval)
  Server->>Store: get_lock_meta(entry_id)
  Store-->>Server: lock_meta (owner, nonce, timestamp, interval?)
  alt no existing lock
    Server->>Store: set_lock_meta(new_owner, nonce)
    Server->>Server: update_lock_timestamp(entry_id)
    Server->>Server: update_lock_interval(entry_id, heartbeatInterval?)
    Server-->>Browser: grant_lock(success)
  else existing lock and not stale
    Server->>Server: is_lock_stale(entry_id) => false
    Server-->>Browser: deny_lock(pending/queued)
  else existing lock and stale
    Server->>Store: delete_lock_meta(entry_id)  -- clears meta, timestamp, interval
    Server->>Store: clear_pending_requests(entry_id)
    Server->>Store: set_lock_meta(new_owner, nonce)
    Server->>Server: update_lock_timestamp(entry_id)
    Server->>Server: update_lock_interval(entry_id, heartbeatInterval?)
    Server-->>Browser: grant_lock(success) + emit lock_granted_stale(entry_id)
  end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review is_lock_stale() threshold math and unit tests for edge cases (timestamp vs interval).
  • Verify meta key consistency for timestamp/interval prefixes and no collision with existing meta.
  • Inspect heartbeat timing/serialization in inline script for correct integer handling and propagation.

Possibly related PRs

  • GravityKit/GravityView#2476 — Implements the same lock-timing constants and methods and similar stale-lock handling; likely an overlapping or related change.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title concisely and accurately describes the main change introduced by the pull request, namely adding an AJAX endpoint for immediate entry lock release on navigation away, aligning with the PR’s core objective and clearly conveying the key enhancement.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9f6f67ba380b292e009070628440d2c8161a0c1 and e338f85cb39f3ac88f38577cd52a64b955b15a02.

📒 Files selected for processing (2)
  • includes/extensions/edit-entry/class-edit-entry-locking.php (11 hunks)
  • readme.txt (1 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Sep 28 '25 15:09 coderabbitai[bot]

@omarkasem this worked super well, thank you!

The only issue I noticed is that after the user that requested access gets control, the "Cancel" button does nothing. Let me know if I need to open a seperate issue about this, thanks!

Screenshot 2025-10-03 at 1 08 20 PM

Mwalek avatar Oct 03 '25 11:10 Mwalek

@Mwalek @mrcasual Thanks for testing. I’m still working on the PR and testing out a few solutions, so I haven’t switched it to “Needs Review” yet. I’ll update the status and request a review once it’s ready.

omarkasem avatar Oct 04 '25 09:10 omarkasem

@Mwalek It's ready for testing now

omarkasem avatar Oct 06 '25 06:10 omarkasem

@omarkasem it works perfectly now, thank you!

cc. @mrcasual

Mwalek avatar Oct 30 '25 10:10 Mwalek