diff --git a/loleaflet/po/templates/loleaflet-ui.pot b/loleaflet/po/templates/loleaflet-ui.pot index d715eca95..55a4f192f 100644 --- a/loleaflet/po/templates/loleaflet-ui.pot +++ b/loleaflet/po/templates/loleaflet-ui.pot @@ -898,16 +898,20 @@ msgstr "" msgid "Start Presentation" msgstr "" -#: src/control/Permission.js:42 +#: src/control/Permission.js:45 msgid "The document could not be locked, and is opened in read-only mode." msgstr "" -#: src/control/Permission.js:44 +#: src/control/Permission.js:47 src/control/Permission.js:65 msgid "" "\n" "Server returned this reason: \"" msgstr "" +#: src/control/Permission.js:63 +msgid "The document could not be locked." +msgstr "" + #: src/control/Ruler.js:366 msgid "Left Margin" msgstr "" diff --git a/loleaflet/src/control/Permission.js b/loleaflet/src/control/Permission.js index 1ba08d90b..62b9bb9ad 100644 --- a/loleaflet/src/control/Permission.js +++ b/loleaflet/src/control/Permission.js @@ -13,24 +13,22 @@ L.Map.include({ var that = this; button.on('click', function () { - button.hide(); - that._enterEditMode('edit'); - that.fire('editorgotfocus'); - // In the iOS/android app, just clicking the mobile-edit-button is - // not reason enough to pop up the on-screen keyboard. - if (!(window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp)) - that.focus(); + that._switchToEditMode(); }); // temporarily, before the user touches the floating action button this._enterReadOnlyMode('readonly'); } + else if (this.options.canTryLock) { + // This is a success response to an attempt to lock using mobile-edit-button + this._switchToEditMode(); + } else { this._enterEditMode(perm); } } else if (perm === 'view' || perm === 'readonly') { - if (window.mode.isMobile() || window.mode.isTablet()) { + if (!this.options.canTryLock && (window.mode.isMobile() || window.mode.isTablet())) { $('#mobile-edit-button').hide(); } @@ -39,13 +37,50 @@ L.Map.include({ }, onLockFailed: function(reason) { - var alertMsg = _('The document could not be locked, and is opened in read-only mode.'); - if (reason) { - alertMsg += _('\nServer returned this reason: "') + reason + '"'; - } + if (this.options.canTryLock === undefined) { + // This is the initial notification. This status is not permanent. + // Allow to try to lock the file for edit again. + this.options.canTryLock = true; - vex.dialog.alert({ message: alertMsg }); - this.options.canTryLock = true; + var alertMsg = _('The document could not be locked, and is opened in read-only mode.'); + if (reason) { + alertMsg += _('\nServer returned this reason: "') + reason + '"'; + } + vex.dialog.alert({ message: alertMsg }); + + var button = $('#mobile-edit-button'); + // TODO: modify the icon here + button.show(); + button.off('click'); + + var that = this; + button.on('click', function () { + that._socket.sendMessage('attemptlock'); + }); + } + else if (this.options.canTryLock) { + // This is a failed response to an attempt to lock using mobile-edit-button + alertMsg = _('The document could not be locked.'); + if (reason) { + alertMsg += _('\nServer returned this reason: "') + reason + '"'; + } + vex.dialog.alert({ message: alertMsg }); + } + // do nothing if this.options.canTryLock is defined and is false + }, + + // from read-only to edit mode + _switchToEditMode: function () { + this.options.canTryLock = false; // don't respond to lockfailed anymore + $('#mobile-edit-button').hide(); + this._enterEditMode('edit'); + if (window.mode.isMobile() || window.mode.isTablet()) { + this.fire('editorgotfocus'); + // In the iOS/android app, just clicking the mobile-edit-button is + // not reason enough to pop up the on-screen keyboard. + if (!(window.ThisIsTheiOSApp || window.ThisIsTheAndroidApp)) + this.focus(); + } }, _enterEditMode: function (perm) { diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 6536b08fd..c75a14a8d 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -736,6 +736,10 @@ bool ClientSession::_handleInput(const char *buffer, int length) return true; } } + else if (tokens.equals(0, "attemptlock")) + { + return attemptLock(docBroker); + } else { LOG_ERR("Session [" << getId() << "] got unknown command [" << tokens[0] << "]."); @@ -992,6 +996,24 @@ void ClientSession::setLockFailed(const std::string& sReason) sendTextFrame("lockfailed:" + sReason); } +bool ClientSession::attemptLock(const std::shared_ptr& docBroker) +{ + if (!isReadOnly()) + return true; + // We are only allowed to change into edit mode if the read-only mode is because of failed lock + if (!_isLockFailed) + return false; + + std::string failReason; + const bool bResult = docBroker->attemptLock(*this, failReason); + if (bResult) + setReadOnly(false); + else + sendTextFrame("lockfailed:" + failReason); + + return bResult; +} + bool ClientSession::hasQueuedMessages() const { return _senderQueue.size() > 0; diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index ab3402935..9c9975c07 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -237,6 +237,9 @@ private: bool isTileInsideVisibleArea(const TileDesc& tile) const; + /// If this session is read-only because of failed lock, try to unlock and make it read-write. + bool attemptLock(const std::shared_ptr& docBroker); + private: std::weak_ptr _docBroker; diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 0de4f15f9..623f3418f 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -910,6 +910,15 @@ bool DocumentBroker::load(const std::shared_ptr& session, const s return true; } +bool DocumentBroker::attemptLock(const ClientSession& session, std::string& failReason) +{ + const bool bResult = _storage->updateLockState(session.getAuthorization(), session.getCookies(), + *_lockCtx, true); + if (!bResult) + failReason = _lockCtx->_lockFailureReason; + return bResult; +} + bool DocumentBroker::saveToStorage(const std::string& sessionId, bool success, const std::string& result, bool force) { diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 24835ceca..b8e176b3c 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -170,6 +170,9 @@ public: /// Notify that the load has completed virtual void setLoaded(); + /// If not yet locked, try to lock + bool attemptLock(const ClientSession& session, std::string& failReason); + bool isDocumentChangedInStorage() { return _documentChangedInStorage; } /// Save the document to Storage if it needs persisting.