0

I'm creating a queue-like page where only 1 person should be able to edit information about a specific database entry at a time.

Let's say a user is on page profile.php?id=1 - only the very first person who loaded this page should be able to make changes to it.

I've attempted to do this with JavaScript/jQuery, but am facing a problem.

On page load, I send an AJAX request to a PHP file that essentially sets a table cell to true for a specific ID. Then, I use a beforeunload event to set this cell to false when the page is unloaded. With this specific implementation, there is a problem though. The beforeunload event is either not fired when the tab is closed, or it is fired and the AJAX request does not have time to complete. This is a big problem because a user could potentially load up the page, see there is nothing to edit, and then close the tab without thinking about it, thus locking the page indefinitely until I reverse the lock in the database manually - and I would hate to have to force people to not use their browser the way they want to.

Here is my code:

$( document ).ready(function() {

    var locked = <?=json_encode($data['in-review']);?>;

    if(locked) {
        $('input').attr('disabled', true);
        $('#delete-model-button').attr('disabled', true);
    $('#uidfield').attr('disabled', true);
        $('#submitbutton').attr('disabled', true).text("This model is in review by another reviewer.");
    } else {
        $.ajax({
            url: 'process/in-review.php',
            type: 'POST',
            data: {uid: $("#uidfield").val(), value: 1},
            success: function (result) {
                console.log("Model Locked in Review");
            }
        });

        $(window).on('beforeunload', function() {
            $.ajax({
                url: 'process/in-review.php',
                type: 'POST',
                data: {uid: $("#uidfield").val(), value: 0},
                success: function (result) {
                    console.log("Model Unlocked");
                }
            });
        });

    }

});

As you can see, if a user loads into a page that is already review locked, the beforeunload process is not called at all because that would allow users to unlock a form that another user may be working on.

How can I effectively unlock these pages if a user closes their tab or browser?

In case it matters, here is my in-review.php page:

<?php

include('db.php');

$uid = $_POST['uid'];
$value = $_POST['value'];

$db->run("UPDATE `devices` SET `in-review`=? WHERE `uid`=?", [$value, $uid]);

--EDIT:

This is commented as a duplicate question, but I disagree with this. That question is specifically asking "Can the unload Event be Used to Reliably fire ajax Request?" - I already know the answer to that is "Yes", because that is the method I am currently using, I also know it does not work at all for closing tabs/browser, therefore my question is not if this is a reliable method for what I want to do (I already know it is not), I'm asking for a different method that I can use to do this.

GrumpyCrouton
  • 8,486
  • 7
  • 32
  • 71
  • @har256b That's not even the same question. I'm not asking about unload events. – GrumpyCrouton May 16 '18 at 15:18
  • You cannot rely on *any* kind of exit event from a browser to do something critical like unlocking a record, there is no event that is guaranteed to fire or to allow AJAX requests to succeed when a user navigates away from a page. You need to rethink the fundamental design of your system, a system that locks records when a user loads a form will never work. You need to lock the record on the server when a user *submits* data, then detect when conflicting data is submitted. – user229044 May 16 '18 at 15:34
  • @meagar Thank you for your comment, the information you have supplied has been noted. Though I still disagree with this being a duplicate, given the reason I supplied in my question before you force closed it. – GrumpyCrouton May 16 '18 at 15:36
  • Meagar is correct. You should re-design. Instead of using a boolean to lock, use a datetime. When checking if it's locked, look at the datetime. If it's past a reasonable time since activity, assume it unlocked. While users are using the page, use javascript prompts with ajax to keep refreshing the date on the lock. – Timothy Kanski May 17 '18 at 20:45

1 Answers1

1

have you tried making a synchronous call of Ajax in beforeunload event?

i.e.

$(window).on('beforeunload', function() {
    $.ajax({
        url: 'process/in-review.php',
        type: 'POST',
        async: false,
        data: {uid: $("#uidfield").val(), value: 0},
        success: function (result) {
            console.log("Model Unlocked");
        }
    });
});

The problem here is browser needs to keep connect alive with the server until the request is completed. When the user clicks on other pages in your application that means connection to the server is alive and operation can be completed, and in case of browser/tab close connection to server is lost.

This might work on some situations i.e. your server was fast enough to respond before complete browser closure i.e. destruction in window object

har256b
  • 118
  • 5
  • This seems to have worked. I understand this may not be super reliable, but I don't really need it to be. I'll probably just make a cron job as well as this method to unlock things if they have been locked for more than an hour or so. As long as this method works most of the time, I think it will be perfectly fine. – GrumpyCrouton May 16 '18 at 15:38
  • yes having a cronjob on the server side will save you from manual database update hell – har256b May 16 '18 at 15:52