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>
This commit is contained in:
Mike Kaganski
2018-02-08 18:06:06 +03:00
parent 7d56128654
commit 2a7057250c
9 changed files with 195 additions and 74 deletions

View File

@@ -162,11 +162,13 @@ public:
bool Commit(); bool Commit();
bool IsStorage(); bool IsStorage();
enum class ShowLockResult { NoLock, Succeeded,Try }; enum class LockFileResult
ShowLockResult ShowLockedDocumentDialog( const LockFileEntry& aData, bool bIsLoading, bool bOwnLock, bool bHandleSysLocked); {
void LockOrigFileOnDemand( bool bLoading, bool bNoUI ); Failed,
enum class MessageDlg { LockFileIgnore, LockFileCorrupt }; FailedLockFile, // there was only lock file that prevented success - no syslock or IO error
bool ShowLockFileProblemDialog(MessageDlg nWhichDlg); Succeeded,
};
LockFileResult LockOrigFileOnDemand( bool bLoading, bool bNoUI, bool bTryIgnoreLockFile = false );
void DisableUnlockWebDAV( bool bDisableUnlockWebDAV = true ); void DisableUnlockWebDAV( bool bDisableUnlockWebDAV = true );
void UnlockFile( bool bReleaseLockStream ); void UnlockFile( bool bReleaseLockStream );
/// Lets Transfer_Impl() not fsync the output file. /// Lets Transfer_Impl() not fsync the output file.
@@ -275,6 +277,13 @@ public:
static bool SetWritableForUserOnly( const OUString& aURL ); static bool SetWritableForUserOnly( const OUString& aURL );
static sal_uInt32 CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter ); static sal_uInt32 CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter );
private:
enum class ShowLockResult { NoLock, Succeeded, Try };
ShowLockResult ShowLockedDocumentDialog(const LockFileEntry& aData, bool bIsLoading, bool bOwnLock, bool bHandleSysLocked);
enum class MessageDlg { LockFileIgnore, LockFileCorrupt };
bool ShowLockFileProblemDialog(MessageDlg nWhichDlg);
}; };
#endif #endif

View File

@@ -235,6 +235,9 @@
#define STR_CANT_CLOSE NC_("STR_CANT_CLOSE", "The document cannot be closed because a\n print job is being carried out.") #define STR_CANT_CLOSE NC_("STR_CANT_CLOSE", "The document cannot be closed because a\n print job is being carried out.")
#define STR_ERROR_SEND_MAIL NC_("STR_ERROR_SEND_MAIL", "An error occurred in sending the message. Possible errors could be a missing user account or a defective setup.\nPlease check the %PRODUCTNAME settings or your e-mail program settings.") #define STR_ERROR_SEND_MAIL NC_("STR_ERROR_SEND_MAIL", "An error occurred in sending the message. Possible errors could be a missing user account or a defective setup.\nPlease check the %PRODUCTNAME settings or your e-mail program settings.")
#define STR_QUERY_OPENASTEMPLATE NC_("STR_QUERY_OPENASTEMPLATE", "This document cannot be edited, possibly due to missing access rights. Do you want to edit a copy of the document?") #define STR_QUERY_OPENASTEMPLATE NC_("STR_QUERY_OPENASTEMPLATE", "This document cannot be edited, possibly due to missing access rights. Do you want to edit a copy of the document?")
#define STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE NC_("STR_QUERY_OPENASTEMPLATE_ALLOW_IGNORE", "This document cannot be edited, because it is locked in another session. Do you want to edit a copy of the document?\n\nYou can also try to ignore the lock and open the file for editing.")
#define STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN NC_("STR_QUERY_OPENASTEMPLATE_OPENCOPY_BTN", "Open ~Copy")
#define STR_QUERY_OPENASTEMPLATE_OPEN_BTN NC_("STR_QUERY_OPENASTEMPLATE_OPEN_BTN", "~Open")
#define STR_REPAIREDDOCUMENT NC_("STR_REPAIREDDOCUMENT", " (repaired document)") #define STR_REPAIREDDOCUMENT NC_("STR_REPAIREDDOCUMENT", " (repaired document)")
#define STR_NONCHECKEDOUT_DOCUMENT NC_("STR_NONCHECKEDOUT_DOCUMENT", "This document is not checked out on the server.") #define STR_NONCHECKEDOUT_DOCUMENT NC_("STR_NONCHECKEDOUT_DOCUMENT", "This document is not checked out on the server.")
#define STR_READONLY_DOCUMENT NC_("STR_READONLY_DOCUMENT", "This document is open in read-only mode.") #define STR_READONLY_DOCUMENT NC_("STR_READONLY_DOCUMENT", "This document is open in read-only mode.")

View File

@@ -846,6 +846,8 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEnt
OUString aInfo; OUString aInfo;
::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl; ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;
sal_Int32 nContinuations = 3;
if ( bOwnLock ) if ( bOwnLock )
{ {
aInfo = aData[LockFileComponent::EDITTIME]; aInfo = aData[LockFileComponent::EDITTIME];
@@ -869,12 +871,23 @@ SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog( const LockFileEnt
xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny( xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) ); 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[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() ); aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
aContinuations[2] = new ::ucbhelper::InteractionDisapprove( 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 ); xInteractionRequestImpl->setContinuations( aContinuations );
xHandler->handle( xInteractionRequestImpl.get() ); 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 // own lock on saving, user has selected to ignore the lock
// alien lock on loading, user has selected to edit a copy of document // 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 // 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 // means that a copy of the document should be opened
GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) ); GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
} }
else if ( bOwnLock ) else
nResult = ShowLockResult::Succeeded; 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] ) else // if ( XSelected == aContinuations[1] )
{ {
// own lock on loading, user has selected to open readonly // 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 // sets SID_DOC_READONLY if the document cannot be opened for editing
// if user cancel the loading the ERROR_ABORT is set // 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 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
(void) bLoading; (void) bLoading;
(void) bNoUI; (void) bNoUI;
(void) bTryIgnoreLockFile;
return LockFileResult::Succeeded;
#else #else
LockFileResult eResult = LockFileResult::Failed;
// check if path scheme is http:// or https:// // check if path scheme is http:// or https://
// may be this is better if used always, in Android and iOS as well? // 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 ! // 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 ) if ( !bResult && !bNoUI )
{ {
bUIStatus = ShowLockedDocumentDialog( aLockData, bLoading, false , false ); bUIStatus = ShowLockedDocumentDialog( aLockData, bLoading, false , true );
} }
} }
catch( ucb::InteractiveNetworkWriteException& ) catch( ucb::InteractiveNetworkWriteException& )
@@ -1108,23 +1130,28 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI )
// when the file is locked, get the current file date // when the file is locked, get the current file date
if ( bResult && DocNeedsFileDateCheck() ) if ( bResult && DocNeedsFileDateCheck() )
GetInitFileDate( true ); GetInitFileDate( true );
if ( bResult )
eResult = LockFileResult::Succeeded;
} }
catch ( const uno::Exception& ) catch ( const uno::Exception& )
{ {
SAL_WARN( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" ); SAL_WARN( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
} }
return; return eResult;
} }
if (!IsLockingUsed() || GetURLObject().HasError()) if (!IsLockingUsed())
return; return LockFileResult::Succeeded;
if (GetURLObject().HasError())
return eResult;
try try
{ {
if ( pImpl->m_bLocked && bLoading if ( pImpl->m_bLocked && bLoading
&& GetURLObject().GetProtocol() == INetProtocol::File ) && 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 // check whether the system file locking should be taken again
GetLockingStream_Impl(); GetLockingStream_Impl();
} }
@@ -1176,7 +1203,7 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI )
// let the stream be opened to check the system file locking // let the stream be opened to check the system file locking
GetMedium_Impl(); GetMedium_Impl();
if (GetError() != ERRCODE_NONE) { if (GetError() != ERRCODE_NONE) {
return; return eResult;
} }
} }
@@ -1202,15 +1229,6 @@ void SfxMedium::LockOrigFileOnDemand( bool bLoading, bool bNoUI )
{ {
bResult = aLockFile.CreateOwnLockFile(); 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&) catch (const uno::Exception&)
{ {
if (bLoading && !bNoUI) 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 ) if ( bUIStatus == ShowLockResult::Succeeded )
{ {
// take the ownership over the lock file // take the ownership over the lock file
bResult = aLockFile.OverwriteOwnLockFile(); 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 // when the file is locked, get the current file date
if ( bResult && DocNeedsFileDateCheck() ) if ( bResult && DocNeedsFileDateCheck() )
GetInitFileDate( true ); GetInitFileDate( true );
if ( bResult )
eResult = LockFileResult::Succeeded;
} }
catch( const uno::Exception& ) catch( const uno::Exception& )
{ {
SAL_WARN( "sfx.doc", "Locking exception: high probability, that the content has not been created" ); SAL_WARN( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
} }
return eResult;
#endif #endif
} }

View File

@@ -134,6 +134,7 @@ using ::com::sun::star::container::XIndexContainer;
#include <sfx2/minfitem.hxx> #include <sfx2/minfitem.hxx>
#include <sfx2/strings.hrc> #include <sfx2/strings.hrc>
#include "impviewframe.hxx" #include "impviewframe.hxx"
#include <vcl/msgbox.hxx>
#define SfxViewFrame #define SfxViewFrame
#include <sfxslots.hxx> #include <sfxslots.hxx>
@@ -151,6 +152,7 @@ void SfxViewFrame::InitInterface_Impl()
#endif #endif
} }
namespace {
/// Asks the user if editing a read-only document is really wanted. /// Asks the user if editing a read-only document is really wanted.
class SfxEditDocumentDialog : public MessageDialog class SfxEditDocumentDialog : public MessageDialog
{ {
@@ -183,8 +185,31 @@ void SfxEditDocumentDialog::dispose()
MessageDialog::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? /// Is this read-only object shell opened via .uno:SignPDF?
static bool IsSignPDF(const SfxObjectShellRef& xObjSh) bool IsSignPDF(const SfxObjectShellRef& xObjSh)
{ {
if (!xObjSh.is()) if (!xObjSh.is())
return false; return false;
@@ -200,7 +225,7 @@ static bool IsSignPDF(const SfxObjectShellRef& xObjSh)
return false; 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 // TODO/LATER: In future the info should replace the direct hash completely
bool bResult = ( !nPasswordHash && !aInfo.getLength() ); bool bResult = ( !nPasswordHash && !aInfo.getLength() );
@@ -248,6 +273,7 @@ static bool AskPasswordToModify_Impl( const uno::Reference< task::XInteractionHa
return bResult; return bResult;
} }
}
void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq ) 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 )) if( !pSh || !pSh->HasName() || !(pSh->Get_Impl()->nLoadedFlags & SfxLoadedFlags::MAINDOCUMENT ))
break; 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(); SfxMedium* pMed = pSh->GetMedium();
const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false); 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; nOpenMode = SFX_STREAM_READONLY;
pSh->SetReadOnlyUI(); aReadOnlyUIGuard.m_bSetRO = true;
} }
else else
{ {
@@ -332,10 +375,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
pSh->SetModifyPasswordEntered(); pSh->SetModifyPasswordEntered();
} }
// Remove infobar if document was read-only (after password check)
RemoveInfoBar("readonly");
nOpenMode = pSh->IsOriginallyReadOnlyMedium() ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE; 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 only the view was in the readonly mode then there is no need to do the reload
if ( !pSh->IsReadOnlyMedium() ) if ( !pSh->IsReadOnlyMedium() )
@@ -344,12 +385,8 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
// open mode among other things, so call SetOpenMode before // open mode among other things, so call SetOpenMode before
// SetReadOnlyUI: // SetReadOnlyUI:
pMed->SetOpenMode( nOpenMode ); pMed->SetOpenMode( nOpenMode );
pSh->SetReadOnlyUI( false );
return; return;
} }
pSh->SetReadOnlyUI( false );
} }
if ( rReq.IsAPI() ) if ( rReq.IsAPI() )
@@ -396,31 +433,58 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
// <- tdf#82744 // <- tdf#82744
{ {
bool bOK = false; bool bOK = false;
if ( !pVersionItem ) bool bRetryIgnoringLock = false;
{ bool bOpenTemplate = false;
bool bHasStorage = pMed->HasStorage_Impl(); do {
// switching edit mode could be possible without reload if ( !pVersionItem )
if ( bHasStorage && pMed->GetStorage() == pSh->GetStorage() )
{ {
// TODO/LATER: faster creation of copy if (bRetryIgnoringLock)
if ( !pSh->ConnectTmpStorage_Impl( pMed->GetStorage(), pMed ) ) pMed->ResetError();
return;
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 )
{
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;
} }
pMed->CloseAndRelease(); if( !bOK )
pMed->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) ); {
pMed->SetOpenMode( nOpenMode ); if (nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI())
{
// css::sdbcx::User offering to open it as a template
ScopedVclPtrInstance<SfxQueryOpenAsTemplate> aBox(&GetWindow(), MessBoxStyle::NONE, bRetryIgnoringLock);
pMed->CompleteReOpen(); short nUserAnswer = aBox->Execute();
if ( nOpenMode & StreamMode::WRITE ) bOpenTemplate = RET_YES == nUserAnswer;
pMed->LockOrigFileOnDemand( false, true ); // Always reset this here to avoid infinite loop
bRetryIgnoringLock = RET_IGNORE == nUserAnswer;
// LockOrigFileOnDemand might set the readonly flag itself, it should be set back }
pMed->GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, !( nOpenMode & StreamMode::WRITE ) ) ); else
bRetryIgnoringLock = false;
if ( !pMed->GetErrorCode() ) }
bOK = true;
} }
while ( !bOK && bRetryIgnoringLock );
if( !bOK ) if( !bOK )
{ {
@@ -440,10 +504,7 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
if ( nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI() ) if ( nOpenMode == SFX_STREAM_READWRITE && !rReq.IsAPI() )
{ {
// css::sdbcx::User offering to open it as a template if ( bOpenTemplate )
ScopedVclPtrInstance<MessageDialog> aBox(&GetWindow(), SfxResId(STR_QUERY_OPENASTEMPLATE),
VclMessageType::Question, VclButtonsType::YesNo);
if ( RET_YES == aBox->Execute() )
{ {
SfxApplication* pApp = SfxGetpApp(); SfxApplication* pApp = SfxGetpApp();
SfxAllItemSet aSet( pApp->GetPool() ); SfxAllItemSet aSet( pApp->GetPool() );
@@ -466,10 +527,13 @@ void SfxViewFrame::ExecReload_Impl( SfxRequest& rReq )
GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet ); GetDispatcher()->Execute( SID_OPENDOC, SfxCallMode::ASYNCHRON, aSet );
return; return;
} }
else
nErr = ERRCODE_NONE; nErr = ERRCODE_NONE;
} }
// Keep the read-only UI
aReadOnlyUIGuard.m_bSetRO = true;
ErrorHandler::HandleError( nErr ); ErrorHandler::HandleError( nErr );
rReq.SetReturnValue( rReq.SetReturnValue(
SfxBoolItem( rReq.GetSlot(), false ) ); SfxBoolItem( rReq.GetSlot(), false ) );

View File

@@ -47,7 +47,8 @@
#define STR_LOCKFAILED_OPENREADONLY_BTN NC_("STR_LOCKFAILED_OPENREADONLY_BTN", "Open ~Read-Only") #define STR_LOCKFAILED_OPENREADONLY_BTN NC_("STR_LOCKFAILED_OPENREADONLY_BTN", "Open ~Read-Only")
#define STR_OPENLOCKED_TITLE NC_("STR_OPENLOCKED_TITLE", "Document in Use") #define STR_OPENLOCKED_TITLE NC_("STR_OPENLOCKED_TITLE", "Document in Use")
#define STR_OPENLOCKED_MSG NC_("STR_OPENLOCKED_MSG", "Document file '$(ARG1)' is locked for editing by:\n\n$(ARG2)\n\nOpen document read-only or open a copy of the document for editing.\n\n") #define STR_OPENLOCKED_MSG NC_("STR_OPENLOCKED_MSG", "Document file '$(ARG1)' is locked for editing by:\n\n$(ARG2)\n\nOpen document read-only or open a copy of the document for editing.\n\n$(ARG3)")
#define STR_OPENLOCKED_ALLOWIGNORE_MSG NC_("STR_OPENLOCKED_ALLOWIGNORE_MSG", "You may also ignore the file locking and open the document for editing.\n\n")
#define STR_OPENLOCKED_OPENREADONLY_BTN NC_("STR_OPENLOCKED_OPENREADONLY_BTN", "Open ~Read-Only") #define STR_OPENLOCKED_OPENREADONLY_BTN NC_("STR_OPENLOCKED_OPENREADONLY_BTN", "Open ~Read-Only")
#define STR_OPENLOCKED_OPENCOPY_BTN NC_("STR_OPENLOCKED_OPENCOPY_BTN", "Open ~Copy") #define STR_OPENLOCKED_OPENCOPY_BTN NC_("STR_OPENLOCKED_OPENCOPY_BTN", "Open ~Copy")
#define STR_UNKNOWNUSER NC_("STR_UNKNOWNUSER", "Unknown User") #define STR_UNKNOWNUSER NC_("STR_UNKNOWNUSER", "Unknown User")

View File

@@ -29,6 +29,7 @@
#include <com/sun/star/task/XInteractionDisapprove.hpp> #include <com/sun/star/task/XInteractionDisapprove.hpp>
#include <com/sun/star/task/XInteractionAbort.hpp> #include <com/sun/star/task/XInteractionAbort.hpp>
#include <com/sun/star/task/XInteractionRequest.hpp> #include <com/sun/star/task/XInteractionRequest.hpp>
#include <com/sun/star/task/XInteractionRetry.hpp>
#include <unotools/resmgr.hxx> #include <unotools/resmgr.hxx>
#include <vcl/svapp.hxx> #include <vcl/svapp.hxx>
@@ -66,7 +67,9 @@ handleLockedDocumentRequest_(
uno::Reference< task::XInteractionApprove > xApprove; uno::Reference< task::XInteractionApprove > xApprove;
uno::Reference< task::XInteractionDisapprove > xDisapprove; uno::Reference< task::XInteractionDisapprove > xDisapprove;
uno::Reference< task::XInteractionAbort > xAbort; uno::Reference< task::XInteractionAbort > xAbort;
getContinuations(rContinuations, &xApprove, &xDisapprove, &xAbort); // In case an option to ignore lock and open the file is available
uno::Reference< task::XInteractionRetry > xRetry;
getContinuations(rContinuations, &xApprove, &xDisapprove, &xAbort, &xRetry);
if ( !xApprove.is() || !xDisapprove.is() || !xAbort.is() ) if ( !xApprove.is() || !xDisapprove.is() || !xAbort.is() )
return; return;
@@ -86,11 +89,14 @@ handleLockedDocumentRequest_(
aArguments.push_back( !aInfo.isEmpty() aArguments.push_back( !aInfo.isEmpty()
? aInfo ? aInfo
: Translate::get( STR_UNKNOWNUSER, aResLocale) ); : Translate::get( STR_UNKNOWNUSER, aResLocale) );
aArguments.push_back( xRetry.is()
? Translate::get( STR_OPENLOCKED_ALLOWIGNORE_MSG, aResLocale )
: "" );
aMessage = Translate::get(STR_OPENLOCKED_MSG, aResLocale); aMessage = Translate::get(STR_OPENLOCKED_MSG, aResLocale);
aMessage = UUIInteractionHelper::replaceMessageWithArguments( aMessage = UUIInteractionHelper::replaceMessageWithArguments(
aMessage, aArguments ); aMessage, aArguments );
ScopedVclPtrInstance< OpenLockedQueryBox > xDialog(pParent, aResLocale, aMessage); ScopedVclPtrInstance< OpenLockedQueryBox > xDialog(pParent, aResLocale, aMessage, xRetry.is());
nResult = xDialog->Execute(); nResult = xDialog->Execute();
} }
else if ( nMode == UUI_DOC_SAVE_LOCK ) else if ( nMode == UUI_DOC_SAVE_LOCK )
@@ -128,6 +134,8 @@ handleLockedDocumentRequest_(
xApprove->select(); xApprove->select();
else if ( nResult == RET_NO ) else if ( nResult == RET_NO )
xDisapprove->select(); xDisapprove->select();
else if ( nResult == RET_IGNORE && xRetry.is() )
xRetry->select();
else else
xAbort->select(); xAbort->select();
} }

View File

@@ -238,10 +238,11 @@ UUIInteractionHelper::replaceMessageWithArguments(
OUString aMessage = _aMessage; OUString aMessage = _aMessage;
SAL_WARN_IF(rArguments.size() == 0, "uui", "replaceMessageWithArguments: No arguments passed!"); SAL_WARN_IF(rArguments.size() == 0, "uui", "replaceMessageWithArguments: No arguments passed!");
if (rArguments.size() > 0) for (size_t i = 0; i < rArguments.size(); ++i)
aMessage = aMessage.replaceAll("$(ARG1)", rArguments[0]); {
if (rArguments.size() > 1) const OUString sReplaceTemplate = "$(ARG" + OUString::number(i+1) + ")";
aMessage = aMessage.replaceAll("$(ARG2)", rArguments[1]); aMessage = aMessage.replaceAll(sReplaceTemplate, rArguments[i]);
}
return aMessage; return aMessage;
} }

View File

@@ -21,7 +21,7 @@
#include "openlocked.hxx" #include "openlocked.hxx"
#include <unotools/resmgr.hxx> #include <unotools/resmgr.hxx>
OpenLockedQueryBox::OpenLockedQueryBox( vcl::Window* pParent, const std::locale& rResLocale, const OUString& aMessage ) : OpenLockedQueryBox::OpenLockedQueryBox( vcl::Window* pParent, const std::locale& rResLocale, const OUString& aMessage, bool bEnableOverride ) :
MessBox(pParent, MessBoxStyle::NONE, 0, MessBox(pParent, MessBoxStyle::NONE, 0,
Translate::get(STR_OPENLOCKED_TITLE, rResLocale), Translate::get(STR_OPENLOCKED_TITLE, rResLocale),
aMessage ) aMessage )
@@ -30,13 +30,19 @@ OpenLockedQueryBox::OpenLockedQueryBox( vcl::Window* pParent, const std::locale&
AddButton(Translate::get(STR_OPENLOCKED_OPENREADONLY_BTN, rResLocale), RET_YES, AddButton(Translate::get(STR_OPENLOCKED_OPENREADONLY_BTN, rResLocale), RET_YES,
ButtonDialogFlags::Default | ButtonDialogFlags::OK | ButtonDialogFlags::Focus); ButtonDialogFlags::Default | ButtonDialogFlags::OK | ButtonDialogFlags::Focus);
SetButtonHelpText(RET_YES, OUString());
AddButton(Translate::get(STR_OPENLOCKED_OPENCOPY_BTN, rResLocale), RET_NO); AddButton(Translate::get(STR_OPENLOCKED_OPENCOPY_BTN, rResLocale), RET_NO);
SetButtonHelpText(RET_NO, OUString());
if (bEnableOverride)
{
// Present option to ignore the (stale?) lock file and open the document
AddButton(Translate::get(STR_ALREADYOPEN_OPEN_BTN, rResLocale), RET_IGNORE);
SetButtonHelpText(RET_IGNORE, OUString());
}
AddButton( StandardButtonType::Cancel, RET_CANCEL, ButtonDialogFlags::Cancel ); AddButton( StandardButtonType::Cancel, RET_CANCEL, ButtonDialogFlags::Cancel );
SetButtonHelpText( RET_YES, OUString() );
SetButtonHelpText( RET_NO, OUString() );
} }
OpenLockedQueryBox::~OpenLockedQueryBox() OpenLockedQueryBox::~OpenLockedQueryBox()

View File

@@ -24,7 +24,7 @@
class OpenLockedQueryBox : public MessBox class OpenLockedQueryBox : public MessBox
{ {
public: public:
OpenLockedQueryBox(vcl::Window* pParent, const std::locale& rResLocale, const OUString& rMessage); OpenLockedQueryBox(vcl::Window* pParent, const std::locale& rResLocale, const OUString& rMessage, bool bEnableOverride);
virtual ~OpenLockedQueryBox() override; virtual ~OpenLockedQueryBox() override;
}; };