/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal. class SwDataSourceRemovedListener : public cppu::WeakImplHelper<sdb::XDatabaseRegistrationsListener>
{
uno::Reference<sdb::XDatabaseContext> m_xDatabaseContext;
SwDBManager* m_pDBManager;
// The revoked database location is inside this document, then remove the // embedding, as otherwise it would be back on the next reload of the // document.
pDocShell->GetStorage()->removeElement(m_pDBManager->getEmbeddedName());
m_pDBManager->setEmbeddedName(OUString(), *pDocShell);
}
m_pImpl->pMergeData.reset(new SwDSParam(aData, xResSet, aSelection));
SwDSParam* pTemp = FindDSData(aData, false); if(pTemp)
*pTemp = *m_pImpl->pMergeData; else
{ // calls from the calculator may have added a connection with an invalid commandtype //"real" data base connections added here have to re-use the already available //DSData and set the correct CommandType
aData.nCommandType = -1;
pTemp = FindDSData(aData, false); if(pTemp)
*pTemp = *m_pImpl->pMergeData; else
{
m_DataSourceParams.push_back(std::make_unique<SwDSParam>(*m_pImpl->pMergeData)); try
{
uno::Reference<lang::XComponent> xComponent(m_DataSourceParams.back()->xConnection, uno::UNO_QUERY); if(xComponent.is())
xComponent->addEventListener(m_pImpl->m_xDisposeListener);
} catch(const uno::Exception&)
{
}
}
} if(!m_pImpl->pMergeData->xConnection.is())
m_pImpl->pMergeData->xConnection = xConnection; // add an XEventListener
case DBMGR_MERGE_PRINTER: case DBMGR_MERGE_EMAIL: case DBMGR_MERGE_FILE: case DBMGR_MERGE_SHELL: // save files and send them as e-Mail if required
bRet = MergeMailFiles(pWorkShell, rMergeDesc); break;
// copy required, m_DataSourceParams can be modified while disposing components
std::vector<uno::Reference<sdbc::XConnection>> aCopiedConnections; for (constauto & pParam : m_DataSourceParams)
{ if(pParam->xConnection.is())
{
aCopiedConnections.push_back(pParam->xConnection);
}
} for (constauto & xConnection : aCopiedConnections)
{ try
{
uno::Reference<lang::XComponent> xComp(xConnection, uno::UNO_QUERY); if(xComp.is())
xComp->dispose();
} catch(const uno::RuntimeException&)
{ //may be disposed already since multiple entries may have used the same connection
}
}
}
// convert fields to text if we are exporting to PDF. // this prevents a second merge while updating the fields // in SwXTextDocument::getRendererCount() if( bIsPDFexport )
rWorkShell.ConvertFieldsToText();
bool bAnyError = !xObjectShell->DoSaveAs(*pDstMed); // Actually this should be a bool... so in case of email and individual // files, where this is set, we skip the recently used handling
bAnyError |= !xObjectShell->DoSaveCompleted( pDstMed, !decodedURL );
bAnyError |= (ERRCODE_NONE != xObjectShell->GetErrorIgnoreWarning()); if( bAnyError )
{ // error message ??
ErrorHandler::HandleError( xObjectShell->GetErrorIgnoreWarning() );
} return !bAnyError;
}
staticvoid lcl_PreparePrinterOptions( const uno::Sequence< beans::PropertyValue >& rInPrintOptions,
uno::Sequence< beans::PropertyValue >& rOutPrintOptions)
{ // printing should be done synchronously otherwise the document // might already become invalid during the process
if( pSourceWindow )
{ // the created window has to be located at the same position as the source window
vcl::Window& rTargetWindow = pWorkFrame->GetFrame().GetWindow();
rTargetWindow.SetPosPixel( pSourceWindow->GetPosPixel() );
}
if (SwWrtShell* pWorkWrtShell = pWorkView->GetWrtShellPtr())
{
pWorkWrtShell->GetViewOptions()->SetIdle( false );
pWorkView->AttrChangedNotify(nullptr);// in order for SelectShell to be called
SwDoc* pWorkDoc = pWorkWrtShell->GetDoc();
pWorkDoc->GetIDocumentUndoRedo().DoUndo( false );
pWorkDoc->ReplaceDocumentProperties( *pSourceDoc );
if( aType == WorkingDocType::TARGET )
{
assert( !ppDBManager );
pWorkDoc->SetInMailMerge( true );
pWorkWrtShell->SetLabelDoc( false );
} else
{ // We have to swap the DBmanager of the new doc, so we also need input
assert(ppDBManager && *ppDBManager);
SwDBManager *pWorkDBManager = pWorkDoc->GetDBManager();
pWorkDoc->SetDBManager( *ppDBManager );
*ppDBManager = pWorkDBManager;
if( aType == WorkingDocType::SOURCE )
{ // the GetDBData call constructs the data, if it's missing - kind of const...
pWorkWrtShell->ChgDBData( const_cast<SwDoc*>(pSourceDoc)->GetDBData() ); // some DocumentSettings are currently not copied by SwDoc::CreateCopy
pWorkWrtShell->SetLabelDoc( rSourceWrtShell.IsLabelDoc() );
pWorkDoc->getIDocumentState().ResetModified();
} else
pWorkDoc->getIDocumentLinksAdministration().EmbedAllLinks();
}
/** * Please have a look at the README in the same directory, before you make * larger changes in this function!
*/ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell, const SwMergeDescriptor& rMergeDescriptor)
{ // deconstruct mail merge type for better readability. // uppercase naming is intentional! constbool bMT_EMAIL = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL; constbool bMT_SHELL = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL; constbool bMT_PRINTER = rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER; constbool bMT_FILE = rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE;
//check if the doc is synchronized and contains at least one linked section constbool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFormatCount() > 1; constbool bNeedsTempFiles = ( bMT_EMAIL || bMT_FILE ); constbool bIsMergeSilent = IsMergeSilent();
// setup the output format
std::shared_ptr<const SfxFilter> pStoreToFilter = SwIoSystem::GetFileFilter(
pSourceDocSh->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer(); const OUString* pStoreToFilterOptions = nullptr;
// if a save_to filter is set then use it - otherwise use the default if( bMT_EMAIL && !rMergeDescriptor.bSendAsAttachment )
{
OUString sExtension = rMergeDescriptor.bSendAsHTML ? u"html"_ustr : u"txt"_ustr;
pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT);
} elseif( !rMergeDescriptor.sSaveToFilter.isEmpty())
{
std::shared_ptr<const SfxFilter> pFilter =
pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter ); if(pFilter)
{
pStoreToFilter = std::move(pFilter); if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty())
pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions;
}
} constbool bIsPDFexport = pStoreToFilter && pStoreToFilter->GetFilterName() == "writer_pdf_Export"; constbool bIsMultiFile = bMT_FILE && !bCreateSingleFile;
m_aMergeStatus = MergeStatus::Ok;
// in case of creating a single resulting file this has to be created here
SwView* pTargetView = rMergeDescriptor.pMailMergeConfigItem ?
rMergeDescriptor.pMailMergeConfigItem->GetTargetView() : nullptr;
SwWrtShell* pTargetShell = nullptr;
SfxObjectShellRef xTargetDocShell;
rtl::Reference<SwDoc> pTargetDoc;
if( bCreateSingleFile )
{ // determine the page style and number used at the start of the source document
pSourceShell->SttEndDoc(true);
nStartingPageNo = pSourceShell->GetVirtPageNum();
}
// Progress, to prohibit KeyInputs
SfxProgress aProgress(pSourceDocSh, OUString(), 1);
// lock all dispatchers
SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh); while (pViewFrame)
{
pViewFrame->GetDispatcher()->Lock(true);
pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
}
sal_Int32 nDocNo = 1;
// For single file mode, the number of pages in the target document so far, which is used // by AppendDoc() to adjust position of page-bound objects. Getting this information directly // from the target doc would require repeated layouts of the doc, which is expensive, but // it can be manually computed from the source documents (for which we do layouts, so the page // count is known, and there is a blank page between each of them in the target document). int targetDocPageCount = 0;
// Synchronized docs don't auto-advance the record set, but there is a // "security" check, which will always advance the record set, if there // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
sal_Int32 nRecordPerDoc = pSourceShell->GetDoc()
->getIDocumentFieldsAccess().GetRecordsPerDocument(); if ( bSynchronizedDoc && (nRecordPerDoc > 1) )
--nRecordPerDoc;
assert( nRecordPerDoc > 0 );
// The SfxObjectShell will be closed explicitly later but // it is more safe to use SfxObjectShellLock here
SfxObjectShellLock xWorkDocSh;
SwView* pWorkView = nullptr;
rtl::Reference<SwDoc> pWorkDoc;
SwDBManager* pWorkDocOrigDBManager = nullptr;
SwWrtShell* pWorkShell = nullptr; bool bWorkDocInitialized = false;
do
{
nStartRow = m_pImpl->pMergeData ? m_pImpl->pMergeData->xResultSet->getRow() : 0;
OUString sColumnData;
// Read the indicated data column, which should contain a valid mail // address or an optional file name if( bMT_EMAIL || bColumnName )
{
sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
}
// create a new temporary file name - only done once in case of bCreateSingleFile if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
{
OUString sPrefix = sDescriptorPrefix;
OUString sLeading;
//#i97667# if the name is from a database field then it will be used _as is_ if( bColumnName && !bMT_EMAIL )
{ if (!sColumnData.isEmpty())
sLeading = sColumnData; else
sLeading = u"_"_ustr;
} else
{
INetURLObject aEntry( sPrefix );
sLeading = aEntry.GetBase();
aEntry.removeSegment();
sPrefix = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
}
// Create a copy of the source document and work with that one instead of the source. // If we're not in the single file mode (which requires modifying the document for the merging), // it is enough to do this just once. Currently PDF also has to be treated special. if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport || bIsMultiFile )
{
assert( !xWorkDocSh.Is());
pWorkDocOrigDBManager = this;
xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY,
*pSourceShell, nullptr, &pWorkDocOrigDBManager,
&pWorkView, &pWorkShell, &pWorkDoc ); if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );
// #i69458# lock fields to prevent access to the result set while calculating layout // tdf#92324: and do not unlock: keep document locked during printing to avoid // ExpFields update during printing, generation of preview, etc.
pWorkShell->LockExpFields();
pWorkShell->CalcLayout(); // tdf#121168: Now force correct page descriptions applied to page frames. Without // this, e.g., page frames starting with sections could have page descriptions set // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
pWorkShell->GetViewOptions()->SetIdle(true); for (auto aLayout : pWorkShell->GetDoc()->GetAllLayouts())
{
aLayout->FreezeLayout(false);
aLayout->AllCheckPageDescs();
}
}
// tdf#92324: Allow ExpFields update only by explicit instruction to avoid // database cursor movement on any other fields update, for example during // print preview and other operations if ( pWorkShell->IsExpFieldsLocked() )
pWorkShell->UnlockExpFields();
pWorkShell->SwViewShell::UpdateFields();
pWorkShell->LockExpFields();
pWorkDoc->RemoveInvisibleContent(); // remove of invisible content has influence on page count and so on fields for page count, // therefore layout has to be updated before fields are converted to text
pWorkShell->CalcLayout();
pWorkShell->ConvertFieldsToText();
pWorkShell->SetNumberingRestart(); if( bSynchronizedDoc )
{
lcl_RemoveSectionLinks( *pWorkShell );
}
// Freeze the layouts of the target document after the first inserted // sub-document, to get the correct PageDesc. if(!bFreezedLayouts && bCreateSingleFile)
{ for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
aLayout->FreezeLayout(true);
bFreezedLayouts = true;
}
} while( IsMergeOk() &&
((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord()));
if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() )
{ // Unlock document fields after merge complete
pWorkView->GetWrtShell().UnlockExpFields();
}
// sw::DocumentLayoutManager::CopyLayoutFormat() did not generate // unique fly names, do it here once.
pTargetDoc->SetInMailMerge(false);
pTargetDoc->SetAllUniqueFlyNames();
// Unfreeze target document layouts and correct all PageDescs.
SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
pTargetShell->CalcLayout();
SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
pTargetShell->GetViewOptions()->SetIdle( true );
pTargetDoc->GetIDocumentUndoRedo().DoUndo( true ); for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
{
aLayout->FreezeLayout(false);
aLayout->AllCheckPageDescs();
}
// we also show canceled documents, as long as there was no error if( !IsMergeError() && bMT_SHELL ) // leave docshell available for caller (e.g. MM wizard)
rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView ); elseif( xTargetDocShell.is() )
xTargetDocShell->DoClose();
Application::Reschedule( true );
if (xProgressDlg)
{
xProgressDlg->response(RET_OK);
}
// unlock all dispatchers
pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh); while (pViewFrame)
{
pViewFrame->GetDispatcher()->Lock(false);
pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
}
if( xMailDispatcher.is() )
{ if( IsMergeOk() )
{ // TODO: Instead of polling via an AutoTimer, post an Idle event, // if the main loop has been made thread-safe.
AutoTimer aEmailDispatcherPollTimer("sw::SwDBManager aEmailDispatcherPollTimer");
aEmailDispatcherPollTimer.SetTimeout( 500 );
aEmailDispatcherPollTimer.Start(); while( IsMergeOk() && m_pImpl->m_xLastMessage.is() && !Application::IsQuit())
Application::Yield();
aEmailDispatcherPollTimer.Stop();
}
xMailDispatcher->stop();
xMailDispatcher->shutdown();
}
// remove the temporary files // has to be done after xMailDispatcher is finished, as mails may be // delivered as message attachments! for( const OUString &sFileURL : aFilesToRemove )
SWUnoHelper::UCB_DeleteFile( sFileURL );
} catch (const uno::Exception&)
{ if (xProgressDlg)
{
xProgressDlg->response(RET_CANCEL);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.