diff options
author | Mike Kaganski <mike.kaganski@collabora.com> | 2018-02-08 18:06:06 +0300 |
---|---|---|
committer | Mike Kaganski <mike.kaganski@collabora.com> | 2018-02-09 05:48:58 +0100 |
commit | 2a7057250c8f73fdfb4c65a7525d17e9770459df (patch) | |
tree | 2fb8b54e2e00f731818041449362ec2163f908ad /sfx2 | |
parent | 7d56128654457bfc859217c23a957d1712fd6e1c (diff) |
tdf#108210: Allow to ignore a lock file if there's no filesystem lock
Two cases are handled: when a file is being opened, and when it was
opened read-only already, and one tries to reopen it in edit mode.
The option to ignore locking and open the file anyway is only offered
when there is no filesystem lock present on the file.
Change-Id: I377d3cae4c949ae64d449634acea8fb3f68a5700
Reviewed-on: https://gerrit.libreoffice.org/49448
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'sfx2')
-rw-r--r-- | sfx2/source/doc/docfile.cxx | 71 | ||||
-rw-r--r-- | sfx2/source/view/viewfrm.cxx | 134 |
2 files changed, 149 insertions, 56 deletions
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx index 055cfa41859d..175ab138c5af 100644 --- a/sfx2/source/doc/docfile.cxx +++ b/sfx2/source/doc/docfile.cxx @@ -846,6 +846,8 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEnt OUString aInfo; ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl; + sal_Int32 nContinuations = 3; + if ( bOwnLock ) { aInfo = aData[LockFileComponent::EDITTIME]; @@ -869,12 +871,23 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEnt xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny( document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) ); + + // Use a fourth continuation in case there's no filesystem lock: + // "Ignore lock file and open the document" + if (!bHandleSysLocked) + nContinuations = 4; } - uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( 3 ); + uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations); aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() ); aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() ); aContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() ); + if (nContinuations > 3) + { + // We use InteractionRetry to reflect that user wants to + // ignore the (stale?) alien lock file and open the document + aContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get()); + } xInteractionRequestImpl->setContinuations( aContinuations ); xHandler->handle( xInteractionRequestImpl.get() ); @@ -890,14 +903,19 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEnt // own lock on saving, user has selected to ignore the lock // alien lock on loading, user has selected to edit a copy of document // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location - if ( bIsLoading && !bOwnLock ) + if ( !bOwnLock ) // bIsLoading implied from outermost condition { // means that a copy of the document should be opened GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) ); } - else if ( bOwnLock ) + else nResult = ShowLockResult::Succeeded; } + else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is()) + { + // User decided to ignore the alien (stale?) lock file without filesystem lock + nResult = ShowLockResult::Succeeded; + } else // if ( XSelected == aContinuations[1] ) { // own lock on loading, user has selected to open readonly @@ -992,12 +1010,16 @@ namespace // sets SID_DOC_READONLY if the document cannot be opened for editing // if user cancel the loading the ERROR_ABORT is set -void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI ) +SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI, bool bTryIgnoreLockFile ) { #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT (void) bLoading; (void) bNoUI; + (void) bTryIgnoreLockFile; + return LockFileResult::Succeeded; #else + LockFileResult eResult = LockFileResult::Failed; + // check if path scheme is http:// or https:// // may be this is better if used always, in Android and iOS as well? // if this code should be always there, remember to move the relevant code in UnlockFile method as well ! @@ -1069,7 +1091,7 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI ) if ( !bResult && !bNoUI ) { - bUIStatus = ShowLockedDocumentDialog( aLockData, bLoading, false , false ); + bUIStatus = ShowLockedDocumentDialog( aLockData, bLoading, false , true ); } } catch( ucb::InteractiveNetworkWriteException& ) @@ -1108,23 +1130,28 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI ) // when the file is locked, get the current file date if ( bResult && DocNeedsFileDateCheck() ) GetInitFileDate( true ); + + if ( bResult ) + eResult = LockFileResult::Succeeded; } catch ( const uno::Exception& ) { SAL_WARN( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" ); } - return; + return eResult; } - if (!IsLockingUsed() || GetURLObject().HasError()) - return; + if (!IsLockingUsed()) + return LockFileResult::Succeeded; + if (GetURLObject().HasError()) + return eResult; try { if ( pImpl->m_bLocked && bLoading && GetURLObject().GetProtocol() == INetProtocol::File ) { - // if the document is already locked the system locking might be temporarely off after storing + // if the document is already locked the system locking might be temporarily off after storing // check whether the system file locking should be taken again GetLockingStream_Impl(); } @@ -1176,7 +1203,7 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI ) // let the stream be opened to check the system file locking GetMedium_Impl(); if (GetError() != ERRCODE_NONE) { - return; + return eResult; } } @@ -1202,15 +1229,6 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI ) { bResult = aLockFile.CreateOwnLockFile(); } - catch (const ucb::InteractiveIOException&) - { - if (bLoading && !bNoUI) - { - bIoErr = true; - ShowLockFileProblemDialog(MessageDlg::LockFileIgnore); - bResult = true; // always delete the defect lock-file - } - } catch (const uno::Exception&) { if (bLoading && !bNoUI) @@ -1270,14 +1288,20 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI ) } } - if ( !bResult && !bNoUI && !bIoErr) + if ( !bResult && !bIoErr) { - bUIStatus = ShowLockedDocumentDialog( aData, bLoading, bOwnLock, bHandleSysLocked ); + if (!bNoUI) + bUIStatus = ShowLockedDocumentDialog(aData, bLoading, bOwnLock, bHandleSysLocked); + else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked) + bUIStatus = ShowLockResult::Succeeded; + if ( bUIStatus == ShowLockResult::Succeeded ) { // take the ownership over the lock file bResult = aLockFile.OverwriteOwnLockFile(); } + else if (bLoading && !bHandleSysLocked) + eResult = LockFileResult::FailedLockFile; } } } @@ -1311,11 +1335,16 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI ) // when the file is locked, get the current file date if ( bResult && DocNeedsFileDateCheck() ) GetInitFileDate( true ); + + if ( bResult ) + eResult = LockFileResult::Succeeded; } catch( const uno::Exception& ) { SAL_WARN( "sfx.doc", "Locking exception: high probability, that the content has not been created" ); } + + return eResult; #endif } diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx index 7a5a536bb46e..7fee9f85800f 100644 --- a/sfx2/source/view/viewfrm.cxx +++ b/sfx2/source/view/viewfrm.cxx @@ -134,6 +134,7 @@ using ::com::sun::star::container::XIndexContainer; #include <sfx2/minfitem.hxx> #include <sfx2/strings.hrc> #include "impviewframe.hxx" +#include <vcl/msgbox.hxx> #define SfxViewFrame #include <sfxslots.hxx> @@ -151,6 +152,7 @@ void SfxViewFrame::InitInterface_Impl() #endif } +namespace { /// Asks the user if editing a read-only document is really wanted. class SfxEditDocumentDialog : public MessageDialog { @@ -183,8 +185,31 @@ void SfxEditDocumentDialog::dispose() MessageDialog::dispose(); } +class SfxQueryOpenAsTemplate : public QueryBox +{ +public: + SfxQueryOpenAsTemplate(vcl::Window* pParent, MessBoxStyle nStyle, bool bAllowIgnoreLock); +}; + +SfxQueryOpenAsTemplate::SfxQueryOpenAsTemplate(vcl::Window* pParent, MessBoxStyle nStyle, bool bAllowIgnoreLock) + : QueryBox(pParent, nStyle, SfxResId(bAllowIgnoreLock ? STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE : STR_QUERY_OPENASTEMPLATE)) +{ + AddButton(SfxResId(STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN), RET_YES, + ButtonDialogFlags::Default | ButtonDialogFlags::OK | ButtonDialogFlags::Focus); + SetButtonHelpText(RET_YES, OUString()); + + if (bAllowIgnoreLock) + { + AddButton(SfxResId(STR_QUERY_OPENASTEMPLATE_OPEN_BTN), RET_IGNORE); + SetButtonHelpText(RET_IGNORE, OUString()); + } + + AddButton(StandardButtonType::Cancel, RET_CANCEL); + SetButtonHelpText(RET_CANCEL, OUString()); +} + /// Is this read-only object shell opened via .uno:SignPDF? -static bool IsSignPDF(const SfxObjectShellRef& xObjSh) +bool IsSignPDF(const SfxObjectShellRef& xObjSh) { if (!xObjSh.is()) return false; @@ -200,7 +225,7 @@ static bool IsSignPDF(const SfxObjectShellRef& xObjSh) return false; } -static bool AskPasswordToModify_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& aPath, const std::shared_ptr<const SfxFilter>& pFilter, sal_uInt32 nPasswordHash, const uno::Sequence< beans::PropertyValue >& aInfo ) +bool AskPasswordToModify_Impl( const uno::Reference< task::XInteractionHandler >& xHandler, const OUString& aPath, const std::shared_ptr<const SfxFilter>& pFilter, sal_uInt32 nPasswordHash, const uno::Sequence< beans::PropertyValue >& aInfo ) { // TODO/LATER: In future the info should replace the direct hash completely bool bResult = ( !nPasswordHash && !aInfo.getLength() ); @@ -248,6 +273,7 @@ static bool AskPasswordToModify_Impl( const uno::Reference< task::XInteractionHa return bResult; } +} void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) { @@ -263,6 +289,23 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) if( !pSh || !pSh->HasName() || !(pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT )) break; + // Only change read-only UI and remove info bar when we succeed + struct ReadOnlyUIGuard + { + SfxViewFrame* m_pFrame; + SfxObjectShell* m_pSh; + bool m_bSetRO; + ~ReadOnlyUIGuard() + { + if (m_bSetRO != m_pSh->IsReadOnlyUI()) + { + m_pSh->SetReadOnlyUI(m_bSetRO); + if (!m_bSetRO) + m_pFrame->RemoveInfoBar("readonly"); + } + } + } aReadOnlyUIGuard{ this, pSh, pSh->IsReadOnlyUI() }; + SfxMedium* pMed = pSh->GetMedium(); const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false); @@ -312,7 +355,7 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) } } nOpenMode = SFX_STREAM_READONLY; - pSh->SetReadOnlyUI(); + aReadOnlyUIGuard.m_bSetRO = true; } else { @@ -332,10 +375,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) pSh->SetModifyPasswordEntered(); } - // Remove infobar if document was read-only (after password check) - RemoveInfoBar("readonly"); - nOpenMode = pSh->IsOriginallyReadOnlyMedium() ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE; + aReadOnlyUIGuard.m_bSetRO = false; // if only the view was in the readonly mode then there is no need to do the reload if ( !pSh->IsReadOnlyMedium() ) @@ -344,12 +385,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) // open mode among other things, so call SetOpenMode before // SetReadOnlyUI: pMed->SetOpenMode( nOpenMode ); - pSh->SetReadOnlyUI( false ); return; } - - - pSh->SetReadOnlyUI( false ); } if ( rReq.IsAPI() ) @@ -396,31 +433,58 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) // <- tdf#82744 { bool bOK = false; - if ( !pVersionItem ) - { - bool bHasStorage = pMed->HasStorage_Impl(); - // switching edit mode could be possible without reload - if ( bHasStorage && pMed->GetStorage() == pSh->GetStorage() ) + bool bRetryIgnoringLock = false; + bool bOpenTemplate = false; + do { + if ( !pVersionItem ) { - // TODO/LATER: faster creation of copy - if ( !pSh->ConnectTmpStorage_Impl( pMed->GetStorage(), pMed ) ) - return; - } + if (bRetryIgnoringLock) + pMed->ResetError(); - pMed->CloseAndRelease(); - pMed->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) ); - pMed->SetOpenMode( nOpenMode ); + bool bHasStorage = pMed->HasStorage_Impl(); + // switching edit mode could be possible without reload + if ( bHasStorage && pMed->GetStorage() == pSh->GetStorage() ) + { + // TODO/LATER: faster creation of copy + if ( !pSh->ConnectTmpStorage_Impl( pMed->GetStorage(), pMed ) ) + return; + } + + pMed->CloseAndRelease(); + pMed->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) ); + pMed->SetOpenMode( nOpenMode ); - pMed->CompleteReOpen(); - if ( nOpenMode & StreamMode::WRITE ) - pMed->LockOrigFileOnDemand( false, true ); + pMed->CompleteReOpen(); + if ( nOpenMode & StreamMode::WRITE ) + { + auto eResult = pMed->LockOrigFileOnDemand( true, true, bRetryIgnoringLock ); + bRetryIgnoringLock = eResult == SfxMedium::LockFileResult::FailedLockFile; + } + + // LockOrigFileOnDemand might set the readonly flag itself, it should be set back + pMed->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) ); + + if ( !pMed->GetErrorCode() ) + bOK = true; + } - // LockOrigFileOnDemand might set the readonly flag itself, it should be set back - pMed->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) ); + if( !bOK ) + { + if (nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI()) + { + // css::sdbcx::User offering to open it as a template + ScopedVclPtrInstance<SfxQueryOpenAsTemplate> aBox(&GetWindow(), MessBoxStyle::NONE, bRetryIgnoringLock); - if ( !pMed->GetErrorCode() ) - bOK = true; + short nUserAnswer = aBox->Execute(); + bOpenTemplate = RET_YES == nUserAnswer; + // Always reset this here to avoid infinite loop + bRetryIgnoringLock = RET_IGNORE == nUserAnswer; + } + else + bRetryIgnoringLock = false; + } } + while ( !bOK && bRetryIgnoringLock ); if( !bOK ) { @@ -440,10 +504,7 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) if ( nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI() ) { - // css::sdbcx::User offering to open it as a template - ScopedVclPtrInstance<MessageDialog> aBox(&GetWindow(), SfxResId(STR_QUERY_OPENASTEMPLATE), - VclMessageType::Question, VclButtonsType::YesNo); - if ( RET_YES == aBox->Execute() ) + if ( bOpenTemplate ) { SfxApplication* pApp = SfxGetpApp(); SfxAllItemSet aSet( pApp->GetPool() ); @@ -466,10 +527,13 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet ); return; } - else - nErr = ERRCODE_NONE; + + nErr = ERRCODE_NONE; } + // Keep the read-only UI + aReadOnlyUIGuard.m_bSetRO = true; + ErrorHandler::HandleError( nErr ); rReq.SetReturnValue( SfxBoolItem( rReq.GetSlot(), false ) ); |