Kaydet (Commit) 3c4dfb87 authored tarafından Jan-Marek Glogowski's avatar Jan-Marek Glogowski

tdf#94987 Create directories for temp filenames

Per default a temporary file is construted from a path and a leading
pattern for the filename. For mail merge the filename can be read
from a database column. If the path is not existing, a temporary
directory is created.

Normally the temp file function would fail, if the filename contains
a slash and the sub-directory of the filename doesn't exists as a
subdirectory of path.

To implement tdf#94987, this adds an option to the temp file class
to create the parent directories of the filename pattern.

Change-Id: I02bf34294dac85598ee153d8cfcf00bc5d7775af
üst 260cd3ae
...@@ -66,8 +66,11 @@ public: ...@@ -66,8 +66,11 @@ public:
rLeadingChars="abc" means "abc0","abc1" and so on, depending on existing files in the folder ). rLeadingChars="abc" means "abc0","abc1" and so on, depending on existing files in the folder ).
The extension string may be f.e. ".txt" or "", if no extension string is given, ".tmp" is used The extension string may be f.e. ".txt" or "", if no extension string is given, ".tmp" is used
@param _bStartWithZero If set to false names will be generated like "abc","abc0","abc1" @param _bStartWithZero If set to false names will be generated like "abc","abc0","abc1"
@param bCreateParentDirs If rLeadingChars contains a slash, this will create the required
parent directories.
*/ */
TempFile( const OUString& rLeadingChars, bool _bStartWithZero=true, const OUString* pExtension=nullptr, const OUString* pParent=nullptr); TempFile( const OUString& rLeadingChars, bool _bStartWithZero=true, const OUString* pExtension=nullptr,
const OUString* pParent=nullptr, bool bCreateParentDirs=false );
/** /**
TempFile will be removed from disk in dtor if EnableKillingFile(true) was called before. TempFile will be removed from disk in dtor if EnableKillingFile(true) was called before.
......
...@@ -70,7 +70,8 @@ public: ...@@ -70,7 +70,8 @@ public:
* The 'verify' method actually has to execute the mail merge by * The 'verify' method actually has to execute the mail merge by
* calling executeMailMerge() after modifying the job arguments. * calling executeMailMerge() after modifying the job arguments.
*/ */
void executeMailMergeTest(const char* filename, const char* datasource, const char* tablename, bool file, int selection) void executeMailMergeTest( const char* filename, const char* datasource, const char* tablename,
bool file, int selection, const char* column )
{ {
maMMtestFilename = filename; maMMtestFilename = filename;
header(); header();
...@@ -79,8 +80,9 @@ public: ...@@ -79,8 +80,9 @@ public:
utl::TempFile aTempDir(nullptr, true); utl::TempFile aTempDir(nullptr, true);
const OUString aWorkDir = aTempDir.GetURL(); const OUString aWorkDir = aTempDir.GetURL();
const OUString aURI( m_directories.getURLFromSrc(mpTestDocumentPath) + OUString::createFromAscii(datasource) ); const OUString aURI( m_directories.getURLFromSrc(mpTestDocumentPath) + OUString::createFromAscii(datasource) );
OUString aDBName = registerDBsource( aURI, aWorkDir ); const OUString aPrefix = column ? OUString::createFromAscii( column ) : "LOMM_";
initMailMergeJobAndArgs( filename, tablename, aDBName, "LOMM_", aWorkDir, file, selection ); const OUString aDBName = registerDBsource( aURI, aWorkDir );
initMailMergeJobAndArgs( filename, tablename, aDBName, aPrefix, aWorkDir, file, selection, column != nullptr );
verify(); verify();
finish(); finish();
...@@ -130,7 +132,8 @@ public: ...@@ -130,7 +132,8 @@ public:
} }
void initMailMergeJobAndArgs( const char* filename, const char* tablename, const OUString &aDBName, void initMailMergeJobAndArgs( const char* filename, const char* tablename, const OUString &aDBName,
const OUString &aPrefix, const OUString &aWorkDir, bool file, int nDataSets ) const OUString &aPrefix, const OUString &aWorkDir, bool file, int nDataSets,
const bool bPrefixIsColumn )
{ {
uno::Reference< task::XJob > xJob( getMultiServiceFactory()->createInstance( "com.sun.star.text.MailMerge" ), uno::UNO_QUERY_THROW ); uno::Reference< task::XJob > xJob( getMultiServiceFactory()->createInstance( "com.sun.star.text.MailMerge" ), uno::UNO_QUERY_THROW );
mxJob.set( xJob ); mxJob.set( xJob );
...@@ -144,6 +147,9 @@ public: ...@@ -144,6 +147,9 @@ public:
args.push_back( beans::NamedValue( OUString( UNO_NAME_OUTPUT_URL ), uno::Any( aWorkDir ) ) ); args.push_back( beans::NamedValue( OUString( UNO_NAME_OUTPUT_URL ), uno::Any( aWorkDir ) ) );
args.push_back( beans::NamedValue( OUString( UNO_NAME_FILE_NAME_PREFIX ), uno::Any( aPrefix )) ); args.push_back( beans::NamedValue( OUString( UNO_NAME_FILE_NAME_PREFIX ), uno::Any( aPrefix )) );
if (bPrefixIsColumn)
args.push_back( beans::NamedValue( OUString( UNO_NAME_FILE_NAME_FROM_COLUMN ), uno::Any( true ) ) );
if (tablename) if (tablename)
{ {
args.push_back( beans::NamedValue( OUString( UNO_NAME_DAD_COMMAND_TYPE ), uno::Any( sdb::CommandType::TABLE ) ) ); args.push_back( beans::NamedValue( OUString( UNO_NAME_DAD_COMMAND_TYPE ), uno::Any( sdb::CommandType::TABLE ) ) );
...@@ -174,6 +180,7 @@ public: ...@@ -174,6 +180,7 @@ public:
const beans::NamedValue *pArguments = mSeqMailMergeArgs.getConstArray(); const beans::NamedValue *pArguments = mSeqMailMergeArgs.getConstArray();
bool bOk = true; bool bOk = true;
bool bMMFilenameFromColumn = false;
sal_Int32 nArgs = mSeqMailMergeArgs.getLength(); sal_Int32 nArgs = mSeqMailMergeArgs.getLength();
for (sal_Int32 i = 0; i < nArgs; ++i) { for (sal_Int32 i = 0; i < nArgs; ++i) {
...@@ -187,6 +194,8 @@ public: ...@@ -187,6 +194,8 @@ public:
bOk &= rValue >>= mailMergeOutputPrefix; bOk &= rValue >>= mailMergeOutputPrefix;
else if (rName == UNO_NAME_OUTPUT_TYPE) else if (rName == UNO_NAME_OUTPUT_TYPE)
bOk &= rValue >>= mnCurOutputType; bOk &= rValue >>= mnCurOutputType;
else if (rName == UNO_NAME_FILE_NAME_FROM_COLUMN)
bOk &= rValue >>= bMMFilenameFromColumn;
} }
CPPUNIT_ASSERT(bOk); CPPUNIT_ASSERT(bOk);
...@@ -205,7 +214,8 @@ public: ...@@ -205,7 +214,8 @@ public:
else else
{ {
CPPUNIT_ASSERT(res == true); CPPUNIT_ASSERT(res == true);
loadMailMergeDocument( 0 ); if( !bMMFilenameFromColumn )
loadMailMergeDocument( 0 );
} }
} }
...@@ -221,26 +231,31 @@ public: ...@@ -221,26 +231,31 @@ public:
return parseExportInternal( mailMergeOutputURL + "/" + name, rStreamName ); return parseExportInternal( mailMergeOutputURL + "/" + name, rStreamName );
} }
/** void loadMailMergeDocument( const OUString &filename )
Loads number-th document from mail merge. Requires file output from mail merge.
*/
void loadMailMergeDocument( int number )
{ {
assert( mnCurOutputType == text::MailMergeType::FILE ); assert( mnCurOutputType == text::MailMergeType::FILE );
if (mxComponent.is()) if (mxComponent.is())
mxComponent->dispose(); mxComponent->dispose();
OUString name = mailMergeOutputPrefix + OUString::number( number ) + ".odt";
// Output name early, so in the case of a hang, the name of the hanging input file is visible. // Output name early, so in the case of a hang, the name of the hanging input file is visible.
std::cout << name << ","; std::cout << filename << ",";
mnStartTime = osl_getGlobalTimer(); mnStartTime = osl_getGlobalTimer();
mxComponent = loadFromDesktop(mailMergeOutputURL + "/" + name, "com.sun.star.text.TextDocument"); mxComponent = loadFromDesktop(mailMergeOutputURL + "/" + filename, "com.sun.star.text.TextDocument");
CPPUNIT_ASSERT( mxComponent.is()); CPPUNIT_ASSERT( mxComponent.is());
OString name2 = OUStringToOString( name, RTL_TEXTENCODING_UTF8 ); OString name2 = OUStringToOString( filename, RTL_TEXTENCODING_UTF8 );
discardDumpedLayout(); discardDumpedLayout();
if (mustCalcLayoutOf(name2.getStr())) if (mustCalcLayoutOf(name2.getStr()))
calcLayout(); calcLayout();
} }
/**
Loads number-th document from mail merge. Requires file output from mail merge.
*/
void loadMailMergeDocument( int number )
{
OUString name = mailMergeOutputPrefix + OUString::number( number ) + ".odt";
loadMailMergeDocument( name );
}
protected: protected:
// Returns page number of the first page of a MM document inside the large MM document (used in the SHELL case). // Returns page number of the first page of a MM document inside the large MM document (used in the SHELL case).
int documentStartPageNumber( int document ) const; int documentStartPageNumber( int document ) const;
...@@ -254,7 +269,7 @@ protected: ...@@ -254,7 +269,7 @@ protected:
const char* maMMtestFilename; const char* maMMtestFilename;
}; };
#define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, file, BaseClass, selection) \ #define DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, file, BaseClass, selection, column) \
class TestName : public BaseClass { \ class TestName : public BaseClass { \
protected: \ protected: \
virtual OUString getTestName() override { return OUString(#TestName); } \ virtual OUString getTestName() override { return OUString(#TestName); } \
...@@ -264,7 +279,7 @@ protected: ...@@ -264,7 +279,7 @@ protected:
CPPUNIT_TEST_SUITE_END(); \ CPPUNIT_TEST_SUITE_END(); \
\ \
void MailMerge() { \ void MailMerge() { \
executeMailMergeTest(filename, datasource, tablename, file, selection); \ executeMailMergeTest(filename, datasource, tablename, file, selection, column); \
} \ } \
void verify() override; \ void verify() override; \
}; \ }; \
...@@ -273,14 +288,17 @@ protected: ...@@ -273,14 +288,17 @@ protected:
// Will generate the resulting document in mxMMDocument. // Will generate the resulting document in mxMMDocument.
#define DECLARE_SHELL_MAILMERGE_TEST(TestName, filename, datasource, tablename) \ #define DECLARE_SHELL_MAILMERGE_TEST(TestName, filename, datasource, tablename) \
DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, false, MMTest, 0) DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, false, MMTest, 0, nullptr)
// Will generate documents as files, use loadMailMergeDocument(). // Will generate documents as files, use loadMailMergeDocument().
#define DECLARE_FILE_MAILMERGE_TEST(TestName, filename, datasource, tablename) \ #define DECLARE_FILE_MAILMERGE_TEST(TestName, filename, datasource, tablename) \
DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, true, MMTest, 0) DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, true, MMTest, 0, nullptr)
#define DECLARE_SHELL_MAILMERGE_TEST_SELECTION(TestName, filename, datasource, tablename, selection) \ #define DECLARE_SHELL_MAILMERGE_TEST_SELECTION(TestName, filename, datasource, tablename, selection) \
DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, false, MMTest, selection) DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, false, MMTest, selection, nullptr)
#define DECLARE_FILE_MAILMERGE_TEST_COLUMN(TestName, filename, datasource, tablename, column) \
DECLARE_MAILMERGE_TEST(TestName, filename, datasource, tablename, true, MMTest, 0, column)
int MMTest::documentStartPageNumber( int document ) const int MMTest::documentStartPageNumber( int document ) const
{ // See documentStartPageNumber() . { // See documentStartPageNumber() .
...@@ -568,5 +586,22 @@ DECLARE_SHELL_MAILMERGE_TEST(test_sections_first_last, "sections_first_last.odt" ...@@ -568,5 +586,22 @@ DECLARE_SHELL_MAILMERGE_TEST(test_sections_first_last, "sections_first_last.odt"
} }
} }
DECLARE_FILE_MAILMERGE_TEST_COLUMN(testDirMailMerge, "simple-mail-merge.odt", "10-testing-addresses.ods", "testing-addresses", "Filename")
{
executeMailMerge();
for( int doc = 1;
doc <= 10;
++doc )
{
OUString filename = "sub/lastname" + OUString::number( doc )
+ " firstname" + OUString::number( doc ) + ".odt";
loadMailMergeDocument( filename );
CPPUNIT_ASSERT_EQUAL( 1, getPages());
CPPUNIT_ASSERT_EQUAL( OUString( "Fixed text." ), getRun( getParagraph( 1 ), 1 )->getString());
CPPUNIT_ASSERT_EQUAL( OUString( "lastname" + OUString::number( doc )), getRun( getParagraph( 2 ), 1 )->getString());
CPPUNIT_ASSERT_EQUAL( OUString( "Another fixed text." ), getRun( getParagraph( 3 ), 1 )->getString());
}
}
CPPUNIT_PLUGIN_IMPLEMENT(); CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -1314,7 +1314,6 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell, ...@@ -1314,7 +1314,6 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
{ {
nStartRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0; nStartRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;
OUString sPath = rMergeDescriptor.sPath;
OUString sColumnData; OUString sColumnData;
// Read the indicated data column, which should contain a valid mail // Read the indicated data column, which should contain a valid mail
...@@ -1322,28 +1321,32 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell, ...@@ -1322,28 +1321,32 @@ bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
if( bMT_EMAIL || bColumnName ) if( bMT_EMAIL || bColumnName )
{ {
sColumnData = GetDBField( xColumnProp, aColumnDBFormat ); sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
if( !bMT_EMAIL )
{
if (sColumnData.isEmpty())
sColumnData = "_";
sPath += sColumnData;
}
} }
// create a new temporary file name - only done once in case of bCreateSingleFile // create a new temporary file name - only done once in case of bCreateSingleFile
if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile )) if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
{ {
INetURLObject aEntry(sPath); OUString sPath = rMergeDescriptor.sPath;
OUString sLeading; OUString sLeading;
//#i97667# if the name is from a database field then it will be used _as is_ //#i97667# if the name is from a database field then it will be used _as is_
if( !sColumnData.isEmpty() ) if( bColumnName && !bMT_EMAIL )
sLeading = sColumnData; {
if (!sColumnData.isEmpty())
sLeading = sColumnData;
else
sLeading = "_";
}
else else
{
INetURLObject aEntry( sPath );
sLeading = aEntry.GetBase(); sLeading = aEntry.GetBase();
aEntry.removeSegment(); aEntry.removeSegment();
sPath = aEntry.GetMainURL( INetURLObject::NO_DECODE ); sPath = aEntry.GetMainURL( INetURLObject::NO_DECODE );
}
OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*')); OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
aTempFile.reset( new utl::TempFile(sLeading, sColumnData.isEmpty(), &sExt, &sPath) ); aTempFile.reset( new utl::TempFile(sLeading, sColumnData.isEmpty(), &sExt, &sPath, true) );
if( !aTempFile->IsValid() ) if( !aTempFile->IsValid() )
{ {
ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED ); ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
......
...@@ -225,11 +225,39 @@ private: ...@@ -225,11 +225,39 @@ private:
sal_uInt32 UniqueTokens::globalValue = SAL_MAX_UINT32; sal_uInt32 UniqueTokens::globalValue = SAL_MAX_UINT32;
namespace
{
class TempDirCreatedObserver : public DirectoryCreationObserver
{
public:
virtual void DirectoryCreated(const rtl::OUString& aDirectoryUrl) override
{
File::setAttributes( aDirectoryUrl, osl_File_Attribute_OwnRead |
osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnExe );
};
};
};
OUString lcl_createName( OUString lcl_createName(
const OUString& rLeadingChars, Tokens & tokens, const OUString* pExtension, const OUString& rLeadingChars, Tokens & tokens, const OUString* pExtension,
const OUString* pParent, bool bDirectory, bool bKeep, bool bLock) const OUString* pParent, bool bDirectory, bool bKeep, bool bLock,
bool bCreateParentDirs )
{ {
OUString aName = ConstructTempDir_Impl( pParent ) + rLeadingChars; OUString aName = ConstructTempDir_Impl( pParent );
if ( bCreateParentDirs )
{
sal_Int32 nOffset = rLeadingChars.lastIndexOf("/");
if (-1 != nOffset)
{
OUString aDirName = aName + OUString( rLeadingChars.getStr(), nOffset );
TempDirCreatedObserver observer;
FileBase::RC err = Directory::createPath( aDirName, &observer );
if ( err != FileBase::E_None && err != FileBase::E_EXIST )
return OUString();
}
}
aName += rLeadingChars;
OUString token; OUString token;
while (tokens.next(&token)) while (tokens.next(&token))
{ {
...@@ -306,7 +334,8 @@ OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = t ...@@ -306,7 +334,8 @@ OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = t
aEyeCatcher += aPidString; aEyeCatcher += aPidString;
#endif #endif
UniqueTokens t; UniqueTokens t;
return lcl_createName(aEyeCatcher, t, nullptr, pParent, bDir, bKeep, false); return lcl_createName( aEyeCatcher, t, nullptr, pParent, bDir, bKeep,
false, false);
} }
OUString TempFile::CreateTempName() OUString TempFile::CreateTempName()
...@@ -328,13 +357,16 @@ TempFile::TempFile( const OUString* pParent, bool bDirectory ) ...@@ -328,13 +357,16 @@ TempFile::TempFile( const OUString* pParent, bool bDirectory )
aName = CreateTempName_Impl( pParent, true, bDirectory ); aName = CreateTempName_Impl( pParent, true, bDirectory );
} }
TempFile::TempFile( const OUString& rLeadingChars, bool _bStartWithZero, const OUString* pExtension, const OUString* pParent) TempFile::TempFile( const OUString& rLeadingChars, bool _bStartWithZero,
const OUString* pExtension, const OUString* pParent,
bool bCreateParentDirs )
: pStream( nullptr ) : pStream( nullptr )
, bIsDirectory( false ) , bIsDirectory( false )
, bKillingFileEnabled( false ) , bKillingFileEnabled( false )
{ {
SequentialTokens t(_bStartWithZero); SequentialTokens t(_bStartWithZero);
aName = lcl_createName(rLeadingChars, t, pExtension, pParent, false/*bDirectory*/, true, true); aName = lcl_createName( rLeadingChars, t, pExtension, pParent, false,
true, true, bCreateParentDirs );
} }
TempFile::~TempFile() TempFile::~TempFile()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment