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
This commit is contained in:
@@ -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.
|
||||||
|
Binary file not shown.
@@ -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:
|
|||||||
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:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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:
|
|||||||
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:
|
|||||||
|
|
||||||
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:
|
|||||||
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:
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
CPPUNIT_ASSERT(res == true);
|
CPPUNIT_ASSERT(res == true);
|
||||||
loadMailMergeDocument( 0 );
|
if( !bMMFilenameFromColumn )
|
||||||
|
loadMailMergeDocument( 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,24 +231,29 @@ public:
|
|||||||
return parseExportInternal( mailMergeOutputURL + "/" + name, rStreamName );
|
return parseExportInternal( mailMergeOutputURL + "/" + name, rStreamName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadMailMergeDocument( const OUString &filename )
|
||||||
|
{
|
||||||
|
assert( mnCurOutputType == text::MailMergeType::FILE );
|
||||||
|
if (mxComponent.is())
|
||||||
|
mxComponent->dispose();
|
||||||
|
// Output name early, so in the case of a hang, the name of the hanging input file is visible.
|
||||||
|
std::cout << filename << ",";
|
||||||
|
mnStartTime = osl_getGlobalTimer();
|
||||||
|
mxComponent = loadFromDesktop(mailMergeOutputURL + "/" + filename, "com.sun.star.text.TextDocument");
|
||||||
|
CPPUNIT_ASSERT( mxComponent.is());
|
||||||
|
OString name2 = OUStringToOString( filename, RTL_TEXTENCODING_UTF8 );
|
||||||
|
discardDumpedLayout();
|
||||||
|
if (mustCalcLayoutOf(name2.getStr()))
|
||||||
|
calcLayout();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Loads number-th document from mail merge. Requires file output from mail merge.
|
Loads number-th document from mail merge. Requires file output from mail merge.
|
||||||
*/
|
*/
|
||||||
void loadMailMergeDocument( int number )
|
void loadMailMergeDocument( int number )
|
||||||
{
|
{
|
||||||
assert( mnCurOutputType == text::MailMergeType::FILE );
|
|
||||||
if (mxComponent.is())
|
|
||||||
mxComponent->dispose();
|
|
||||||
OUString name = mailMergeOutputPrefix + OUString::number( number ) + ".odt";
|
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.
|
loadMailMergeDocument( name );
|
||||||
std::cout << name << ",";
|
|
||||||
mnStartTime = osl_getGlobalTimer();
|
|
||||||
mxComponent = loadFromDesktop(mailMergeOutputURL + "/" + name, "com.sun.star.text.TextDocument");
|
|
||||||
CPPUNIT_ASSERT( mxComponent.is());
|
|
||||||
OString name2 = OUStringToOString( name, RTL_TEXTENCODING_UTF8 );
|
|
||||||
discardDumpedLayout();
|
|
||||||
if (mustCalcLayoutOf(name2.getStr()))
|
|
||||||
calcLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
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:
|
|||||||
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:
|
|||||||
|
|
||||||
// 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"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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:
|
|||||||
|
|
||||||
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
|
|||||||
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 )
|
|||||||
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()
|
||||||
|
Reference in New Issue
Block a user