/* -*- 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 .
*/
#include <limits.h>
#include <mutex>
#include <string_view>
#include <com/sun/star/uno/Any.h>
#include <sal/log.hxx>
#include <unotools/pathoptions.hxx>
#include <tools/urlobj.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <ucbhelper/content.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/document/XTypeDetection.hpp>
#include <com/sun/star/document/DocumentProperties.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/DocumentTemplates.hpp>
#include <com/sun/star/frame/XDocumentTemplates.hpp>
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/io/XPersist.hpp>
#include <com/sun/star/lang/XLocalizable.hpp>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/ucb/TransferInfo.hpp>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/ucb/XContentAccess.hpp>
#include <com/sun/star/ucb/AnyCompareFactory.hpp>
#include <com/sun/star/ucb/NumberedSortingInfo.hpp>
#include "doctemplateslocal.hxx"
#include <sfxurlrelocator.hxx>
#include <sfx2/doctempl.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/sfxresid.hxx>
#include <sfx2/strings.hrc>
#include <strings.hxx>
#include <svtools/templatefoldercache.hxx>
#include <memory>
#include <utility>
#include <vector>
using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::frame;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::document;
using namespace ::rtl;
using namespace ::ucbhelper;
constexpr OUString TITLE = u"Title"_ustr;
constexpr OUString TARGET_URL = u"TargetURL"_ustr;
constexpr OUStringLiteral COMMAND_TRANSFER = u"transfer";
namespace {
class RegionData_Impl;
}
namespace DocTempl {
namespace {
class DocTempl_EntryData_Impl
{
RegionData_Impl* mpParent;
// the following member must be SfxObjectShellLock since it controls that SfxObjectShell lifetime by design
// and users of this class expect it to be so.
SfxObjectShellLock mxObjShell;
OUString maTitle;
OUString maOwnURL;
OUString maTargetURL;
public:
DocTempl_EntryData_Impl( RegionData_Impl* pParent,
const OUString& rTitle );
const OUString& GetTitle() const { return maTitle; }
const OUString& GetTargetURL();
const OUString& GetHierarchyURL();
void SetTitle( const OUString& rTitle ) { maTitle = rTitle; }
void SetTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
void SetHierarchyURL( const OUString& rURL) { maOwnURL = rURL; }
int Compare( std::u16string_view rTitle ) const;
};
}
}
using namespace ::DocTempl;
namespace {
class RegionData_Impl
{
const SfxDocTemplate_Impl* mpParent;
std::vector<std::unique_ptr<DocTempl_EntryData_Impl>> maEntries;
OUString maTitle;
OUString maOwnURL;
private:
size_t GetEntryPos( std::u16string_view rTitle,
bool& rFound ) const;
public:
RegionData_Impl( const SfxDocTemplate_Impl* pParent,
OUString aTitle );
void SetHierarchyURL( const OUString& rURL) { maOwnURL = rURL; }
DocTempl_EntryData_Impl* GetEntry( size_t nIndex ) const;
DocTempl_EntryData_Impl* GetEntry( std::u16string_view rName ) const;
const OUString& GetTitle() const { return maTitle; }
const OUString& GetHierarchyURL();
size_t GetCount() const;
void SetTitle( const OUString& rTitle ) { maTitle = rTitle; }
void AddEntry( const OUString& rTitle,
const OUString& rTargetURL,
const size_t *pPos );
void DeleteEntry( size_t nIndex );
int Compare( RegionData_Impl const * pCompareWith ) const;
};
}
class SfxDocTemplate_Impl : public SvRefBase
{
uno::Reference< XPersist > mxInfo;
uno::Reference< XDocumentTemplates > mxTemplates;
std::mutex maMutex;
OUString maRootURL;
OUString maStandardGroup;
std::vector<std::unique_ptr<RegionData_Impl>> maRegions;
bool mbConstructed;
uno::Reference< XAnyCompareFactory > m_rCompareFactory;
// the following member is intended to prevent clearing of the global data when it is in use
// TODO/LATER: it still does not make the implementation complete thread-safe
sal_Int32 mnLockCounter;
private:
void Clear();
public:
SfxDocTemplate_Impl();
virtual ~SfxDocTemplate_Impl() override;
void IncrementLock();
void DecrementLock();
bool Construct( );
void CreateFromHierarchy( std::unique_lock<std::mutex>& rGuard, Content &rTemplRoot );
void ReInitFromComponent();
void AddRegion( std::unique_lock<std::mutex>& rGuard,
const OUString& rTitle,
Content& rContent );
void Rescan();
void DeleteRegion( size_t nIndex );
size_t GetRegionCount() const
{ return maRegions.size(); }
RegionData_Impl* GetRegion( std::u16string_view rName ) const;
RegionData_Impl* GetRegion( size_t nIndex ) const;
bool GetTitleFromURL( const OUString& rURL, OUString& aTitle );
bool InsertRegion( std::unique_ptr<RegionData_Impl> pData, size_t nPos );
const OUString& GetRootURL() const { return maRootURL; }
const uno::Reference< XDocumentTemplates >& getDocTemplates() const { return mxTemplates; }
};
namespace {
class DocTemplLocker_Impl
{
SfxDocTemplate_Impl& m_aDocTempl;
public:
explicit DocTemplLocker_Impl( SfxDocTemplate_Impl& aDocTempl )
: m_aDocTempl( aDocTempl )
{
m_aDocTempl.IncrementLock();
}
~DocTemplLocker_Impl()
{
m_aDocTempl.DecrementLock();
}
};
}
static SfxDocTemplate_Impl *gpTemplateData = nullptr;
static bool getTextProperty_Impl( Content& rContent,
const OUString& rPropName,
OUString& rPropValue );
OUString SfxDocumentTemplates::GetFullRegionName
(
sal_uInt16 nIdx // Region Index
) const
/* [Description]
Returns the logical name of a region and its path
[Return value] Reference to the Region name
*/
{
// First: find the RegionData for the index
DocTemplLocker_Impl aLocker( *pImp );
if ( pImp->Construct() )
{
RegionData_Impl *pData1 = pImp->GetRegion( nIdx );
if ( pData1 )
return pData1->GetTitle();
// --**-- here was some code which appended the path to the
// group if there was more than one with the same name.
// this should not happen anymore
}
return OUString();
}
OUString SfxDocumentTemplates::GetRegionName
(
sal_uInt16 nIdx // Region Index
) const
/* [Description]
Returns the logical name of a region
[Return value]
const String& Reference to the Region name
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( pImp->Construct() )
{
RegionData_Impl *pData = pImp->GetRegion( nIdx );
if ( pData )
return pData->GetTitle();
}
return OUString();
}
sal_uInt16 SfxDocumentTemplates::GetRegionCount() const
/* [Description]
Returns the number of Regions
[Return value]
sal_uInt16 Number of Regions
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( !pImp->Construct() )
return 0;
return pImp->GetRegionCount();
}
sal_uInt16 SfxDocumentTemplates::GetCount
(
sal_uInt16 nRegion /* Region index whose number is
to be determined */
) const
/* [Description]
Number of entries in Region
[Return value] Number of entries
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( !pImp->Construct() )
return 0;
RegionData_Impl *pData = pImp->GetRegion( nRegion );
if ( !pData )
return 0;
return pData->GetCount();
}
OUString SfxDocumentTemplates::GetName
(
sal_uInt16 nRegion, // Region Index, in which the entry lies
sal_uInt16 nIdx // Index of the entry
) const
/* [Description]
Returns the logical name of an entry in Region
[Return value]
const String& Entry Name
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( pImp->Construct() )
{
RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
if ( pRegion )
{
DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
if ( pEntry )
return pEntry->GetTitle();
}
}
return OUString();
}
OUString SfxDocumentTemplates::GetPath
(
sal_uInt16 nRegion, // Region Index, in which the entry lies
sal_uInt16 nIdx // Index of the entry
) const
/* [Description]
Returns the file name with full path to the file assigned to an entry
[Return value]
String File name with full path
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( !pImp->Construct() )
return OUString();
RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
if ( pRegion )
{
DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
if ( pEntry )
return pEntry->GetTargetURL();
}
return OUString();
}
OUString SfxDocumentTemplates::GetTemplateTargetURLFromComponent( std::u16string_view aGroupName,
std::u16string_view aTitle )
{
DocTemplLocker_Impl aLocker( *pImp );
INetURLObject aTemplateObj( pImp->GetRootURL() );
aTemplateObj.insertName( aGroupName, false,
INetURLObject::LAST_SEGMENT,
INetURLObject::EncodeMechanism::All );
aTemplateObj.insertName( aTitle, false,
INetURLObject::LAST_SEGMENT,
INetURLObject::EncodeMechanism::All );
Content aTemplate;
uno::Reference< XCommandEnvironment > aCmdEnv;
if ( Content::create( aTemplateObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
{
OUString aResult;
getTextProperty_Impl( aTemplate, TARGET_URL, aResult );
return SvtPathOptions().SubstituteVariable( aResult );
}
return OUString();
}
/** Convert a template name to its localised pair if it exists.
@param rString
Name to be translated.
@return
The localised pair of rString or rString if the former does not exist.
*/
OUString SfxDocumentTemplates::ConvertResourceString(const OUString& rString)
{
static constexpr OUString aTemplateNames[] =
{
STR_TEMPLATE_NAME1_DEF,
STR_TEMPLATE_NAME2_DEF,
STR_TEMPLATE_NAME3_DEF,
STR_TEMPLATE_NAME4_DEF,
STR_TEMPLATE_NAME5_DEF,
STR_TEMPLATE_NAME6_DEF,
STR_TEMPLATE_NAME7_DEF,
STR_TEMPLATE_NAME8_DEF,
STR_TEMPLATE_NAME9_DEF,
STR_TEMPLATE_NAME10_DEF,
STR_TEMPLATE_NAME11_DEF,
STR_TEMPLATE_NAME12_DEF,
STR_TEMPLATE_NAME13_DEF,
STR_TEMPLATE_NAME14_DEF,
STR_TEMPLATE_NAME15_DEF,
STR_TEMPLATE_NAME16_DEF,
STR_TEMPLATE_NAME17_DEF,
STR_TEMPLATE_NAME18_DEF,
STR_TEMPLATE_NAME19_DEF,
STR_TEMPLATE_NAME20_DEF,
STR_TEMPLATE_NAME21_DEF,
STR_TEMPLATE_NAME22_DEF,
STR_TEMPLATE_NAME23_DEF,
STR_TEMPLATE_NAME24_DEF,
STR_TEMPLATE_NAME25_DEF,
STR_TEMPLATE_NAME26_DEF,
STR_TEMPLATE_NAME27_DEF,
STR_TEMPLATE_NAME28_DEF,
STR_TEMPLATE_NAME29_DEF,
STR_TEMPLATE_NAME30_DEF,
STR_TEMPLATE_NAME31_DEF,
STR_TEMPLATE_NAME32_DEF,
STR_TEMPLATE_NAME33_DEF,
STR_TEMPLATE_NAME34_DEF
};
TranslateId STR_TEMPLATE_NAME[] =
{
STR_TEMPLATE_NAME1,
STR_TEMPLATE_NAME2,
STR_TEMPLATE_NAME3,
STR_TEMPLATE_NAME4,
STR_TEMPLATE_NAME5,
STR_TEMPLATE_NAME6,
STR_TEMPLATE_NAME7,
STR_TEMPLATE_NAME8,
STR_TEMPLATE_NAME9,
STR_TEMPLATE_NAME10,
STR_TEMPLATE_NAME11,
STR_TEMPLATE_NAME12,
STR_TEMPLATE_NAME13,
STR_TEMPLATE_NAME14,
STR_TEMPLATE_NAME15,
STR_TEMPLATE_NAME16,
STR_TEMPLATE_NAME17,
STR_TEMPLATE_NAME18,
STR_TEMPLATE_NAME19,
STR_TEMPLATE_NAME20,
STR_TEMPLATE_NAME21,
STR_TEMPLATE_NAME22,
STR_TEMPLATE_NAME23,
STR_TEMPLATE_NAME24,
STR_TEMPLATE_NAME25,
STR_TEMPLATE_NAME26,
STR_TEMPLATE_NAME27,
STR_TEMPLATE_NAME28,
STR_TEMPLATE_NAME29,
STR_TEMPLATE_NAME30,
STR_TEMPLATE_NAME31,
STR_TEMPLATE_NAME32,
STR_TEMPLATE_NAME33,
STR_TEMPLATE_NAME34
};
static_assert(SAL_N_ELEMENTS(aTemplateNames) == SAL_N_ELEMENTS(STR_TEMPLATE_NAME));
for (size_t i = 0; i < SAL_N_ELEMENTS(STR_TEMPLATE_NAME); ++i)
{
if (rString == aTemplateNames[i])
return SfxResId(STR_TEMPLATE_NAME[i]);
}
return rString;
}
bool SfxDocumentTemplates::CopyOrMove
(
sal_uInt16 nTargetRegion, // Target Region Index
sal_uInt16 nTargetIdx, // Target position Index
sal_uInt16 nSourceRegion, // Source Region Index
sal_uInt16 nSourceIdx, /* Index to be copied / to moved template */
bool bMove // Copy / Move
)
/* [Description]
Copy or move a document template
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::Move(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16)>
<SfxDocumentTemplates::Copy(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16)>
*/
{
/* to perform a copy or move, we need to send a transfer command to
the destination folder with the URL of the source as parameter.
( If the destination content doesn't support the transfer command,
we could try a copy ( and delete ) instead. )
We need two transfers ( one for the real template and one for its
representation in the hierarchy )
...
*/
DocTemplLocker_Impl aLocker( *pImp );
if ( !pImp->Construct() )
return false;
// Don't copy or move any folders
if( nSourceIdx == USHRT_MAX )
return false ;
if ( nSourceRegion == nTargetRegion )
{
SAL_WARN( "sfx.doc", "Don't know, what to do!" );
return false;
}
RegionData_Impl *pSourceRgn = pImp->GetRegion( nSourceRegion );
if ( !pSourceRgn )
return false;
DocTempl_EntryData_Impl *pSource = pSourceRgn->GetEntry( nSourceIdx );
if ( !pSource )
return false;
RegionData_Impl *pTargetRgn = pImp->GetRegion( nTargetRegion );
if ( !pTargetRgn )
return false;
const OUString aTitle = pSource->GetTitle();
uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
if ( xTemplates->addTemplate( pTargetRgn->GetTitle(),
aTitle,
pSource->GetTargetURL() ) )
{
const OUString aNewTargetURL = GetTemplateTargetURLFromComponent( pTargetRgn->GetTitle(), aTitle );
if ( aNewTargetURL.isEmpty() )
return false;
if ( bMove )
{
// --**-- delete the original file
bool bDeleted = xTemplates->removeTemplate( pSourceRgn->GetTitle(),
pSource->GetTitle() );
if ( bDeleted )
pSourceRgn->DeleteEntry( nSourceIdx );
else
{
if ( xTemplates->removeTemplate( pTargetRgn->GetTitle(), aTitle ) )
return false; // will trigger retry with copy instead of move
// if it is not possible to remove just created template ( must be possible! )
// it is better to report success here, since at least the copy has succeeded
// TODO/LATER: solve it more gracefully in future
}
}
// todo: fix SfxDocumentTemplates to handle size_t instead of sal_uInt16
size_t temp_nTargetIdx = nTargetIdx;
pTargetRgn->AddEntry( aTitle, aNewTargetURL, &temp_nTargetIdx );
return true;
}
// --**-- if the current file is opened,
// it must be re-opened afterwards.
return false;
}
bool SfxDocumentTemplates::Move
(
sal_uInt16 nTargetRegion, // Target Region Index
sal_uInt16 nTargetIdx, // Target position Index
sal_uInt16 nSourceRegion, // Source Region Index
sal_uInt16 nSourceIdx /* Index to be copied / to moved template */
)
/* [Description]
Moving a template
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::CopyOrMove(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16,sal_Bool)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
return CopyOrMove( nTargetRegion, nTargetIdx,
nSourceRegion, nSourceIdx, true );
}
bool SfxDocumentTemplates::Copy
(
sal_uInt16 nTargetRegion, // Target Region Index
sal_uInt16 nTargetIdx, // Target position Index
sal_uInt16 nSourceRegion, // Source Region Index
sal_uInt16 nSourceIdx /* Index to be copied / to moved template */
)
/* [Description]
Copying a template
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::CopyOrMove(sal_uInt16,sal_uInt16,sal_uInt16,sal_uInt16,sal_Bool)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
return CopyOrMove( nTargetRegion, nTargetIdx,
nSourceRegion, nSourceIdx, false );
}
bool SfxDocumentTemplates::CopyTo
(
sal_uInt16 nRegion, // Region of the template to be exported
sal_uInt16 nIdx, // Index of the template to be exported
std::u16string_view rName /* File name under which the template is to
be created */
) const
/* [Description]
Exporting a template into the file system
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::CopyFrom(sal_uInt16,sal_uInt16,String&)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( ! pImp->Construct() )
return false;
RegionData_Impl *pSourceRgn = pImp->GetRegion( nRegion );
if ( !pSourceRgn )
return false;
DocTempl_EntryData_Impl *pSource = pSourceRgn->GetEntry( nIdx );
if ( !pSource )
return false;
INetURLObject aTargetURL( rName );
const OUString aTitle( aTargetURL.getName( INetURLObject::LAST_SEGMENT, true,
INetURLObject::DecodeMechanism::WithCharset ) );
aTargetURL.removeSegment();
const OUString aParentURL = aTargetURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
uno::Reference< XCommandEnvironment > aCmdEnv;
Content aTarget;
try
{
aTarget = Content( aParentURL, aCmdEnv, comphelper::getProcessComponentContext() );
TransferInfo aTransferInfo;
aTransferInfo.MoveData = false;
aTransferInfo.SourceURL = pSource->GetTargetURL();
aTransferInfo.NewTitle = aTitle;
aTransferInfo.NameClash = NameClash::RENAME;
Any aArg( aTransferInfo );
aTarget.executeCommand( COMMAND_TRANSFER, aArg );
}
catch ( ContentCreationException& )
{ return false; }
catch ( Exception& )
{ return false; }
return true;
}
bool SfxDocumentTemplates::CopyFrom
(
sal_uInt16 nRegion, /* Region in which the template is to be
imported */
sal_uInt16 nIdx, // Index of the new template in this Region
OUString& rName /* File name of the template to be imported
as an out parameter of the (automatically
generated from the file name) logical name
of the template */
)
/* [Description]
Import a template from the file system
[Return value] Success (sal_True) or serfpTargetDirectory->GetContent());
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::CopyTo(sal_uInt16,sal_uInt16,const String&)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( ! pImp->Construct() )
return false;
RegionData_Impl *pTargetRgn = pImp->GetRegion( nRegion );
if ( !pTargetRgn )
return false;
uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
if ( !xTemplates.is() )
return false;
OUString aTitle;
bool bTemplateAdded = false;
if( pImp->GetTitleFromURL( rName, aTitle ) )
{
bTemplateAdded = xTemplates->addTemplate( pTargetRgn->GetTitle(), aTitle, rName );
}
else
{
uno::Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(u"Hidden"_ustr, true) };
INetURLObject aTemplURL( rName );
uno::Reference< XDocumentPropertiesSupplier > xDocPropsSupplier;
uno::Reference< XStorable > xStorable;
try
{
xStorable.set(
xDesktop->loadComponentFromURL( aTemplURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
u"_blank"_ustr,
0,
aArgs ),
UNO_QUERY );
xDocPropsSupplier.set( xStorable, UNO_QUERY );
}
catch( Exception& )
{
}
if( xStorable.is() )
{
// get Title from XDocumentPropertiesSupplier
if( xDocPropsSupplier.is() )
{
uno::Reference< XDocumentProperties > xDocProps
= xDocPropsSupplier->getDocumentProperties();
if (xDocProps.is() ) {
aTitle = xDocProps->getTitle();
}
}
if( aTitle.isEmpty() )
{
INetURLObject aURL(std::move(aTemplURL));
aURL.CutExtension();
aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
INetURLObject::DecodeMechanism::WithCharset );
}
// write a template using XStorable interface
bTemplateAdded = xTemplates->storeTemplate( pTargetRgn->GetTitle(), aTitle, xStorable );
}
}
if( bTemplateAdded )
{
INetURLObject aTemplObj( pTargetRgn->GetHierarchyURL() );
aTemplObj.insertName( aTitle, false,
INetURLObject::LAST_SEGMENT,
INetURLObject::EncodeMechanism::All );
const OUString aTemplURL = aTemplObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
uno::Reference< XCommandEnvironment > aCmdEnv;
Content aTemplCont;
if( Content::create( aTemplURL, aCmdEnv, comphelper::getProcessComponentContext(), aTemplCont ) )
{
OUString aTemplName;
if( getTextProperty_Impl( aTemplCont, TARGET_URL, aTemplName ) )
{
if ( nIdx == USHRT_MAX )
nIdx = 0;
else
++nIdx;
// todo: fix SfxDocumentTemplates to handle size_t instead of sal_uInt16
size_t temp_nIdx = nIdx;
pTargetRgn->AddEntry( aTitle, aTemplName, &temp_nIdx );
rName = aTitle;
return true;
}
else
{
SAL_WARN( "sfx.doc", "CopyFrom(): The content should contain target URL!" );
}
}
else
{
SAL_WARN( "sfx.doc", "CopyFrom(): The content just was created!" );
}
}
return false;
}
bool SfxDocumentTemplates::Delete
(
sal_uInt16 nRegion, // Region Index
sal_uInt16 nIdx /* Index of the entry or USHRT_MAX,
if a directory is meant. */
)
/* [Description]
Deleting an entry or a directory
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::InsertDir(const String&,sal_uInt16)>
<SfxDocumentTemplates::KillDir(SfxTemplateDir&)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
/* delete the template or folder in the hierarchy and in the
template folder by sending a delete command to the content.
Then remove the data from the lists
*/
if ( ! pImp->Construct() )
return false;
RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
if ( !pRegion )
return false;
bool bRet;
uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
if ( nIdx == USHRT_MAX )
{
bRet = xTemplates->removeGroup( pRegion->GetTitle() );
if ( bRet )
pImp->DeleteRegion( nRegion );
}
else
{
DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
if ( !pEntry )
return false;
bRet = xTemplates->removeTemplate( pRegion->GetTitle(),
pEntry->GetTitle() );
if( bRet )
pRegion->DeleteEntry( nIdx );
}
return bRet;
}
bool SfxDocumentTemplates::InsertDir
(
const OUString& rText, // the logical name of the new Region
sal_uInt16 nRegion // Region Index
)
/* [Description]
Insert an index
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::KillDir(SfxTemplateDir&)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( ! pImp->Construct() )
return false;
RegionData_Impl *pRegion = pImp->GetRegion( rText );
if ( pRegion )
return false;
uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
if ( xTemplates->addGroup( rText ) )
{
return pImp->InsertRegion( std::make_unique<RegionData_Impl>( pImp.get(), rText ), nRegion );
}
return false;
}
bool SfxDocumentTemplates::InsertTemplate(sal_uInt16 nSourceRegion, sal_uInt16 nIdx, const OUString &rName, const OUString &rPath)
{
DocTemplLocker_Impl aLocker( *pImp );
if ( ! pImp->Construct() )
return false;
RegionData_Impl *pRegion = pImp->GetRegion( nSourceRegion );
if ( !pRegion )
return false;
size_t pos = nIdx;
pRegion->AddEntry( rName, rPath, &pos );
return true;
}
bool SfxDocumentTemplates::SetName( const OUString& rName, sal_uInt16 nRegion, sal_uInt16 nIdx )
{
DocTemplLocker_Impl aLocker( *pImp );
if ( ! pImp->Construct() )
return false;
RegionData_Impl *pRegion = pImp->GetRegion( nRegion );
if ( !pRegion )
return false;
uno::Reference< XDocumentTemplates > xTemplates = pImp->getDocTemplates();
if ( nIdx == USHRT_MAX )
{
if ( pRegion->GetTitle() == rName )
return true;
// we have to rename a region
if ( xTemplates->renameGroup( pRegion->GetTitle(), rName ) )
{
pRegion->SetTitle( rName );
pRegion->SetHierarchyURL( u""_ustr );
return true;
}
}
else
{
DocTempl_EntryData_Impl *pEntry = pRegion->GetEntry( nIdx );
if ( !pEntry )
return false;
if ( pEntry->GetTitle() == rName )
return true;
if ( xTemplates->renameTemplate( pRegion->GetTitle(),
pEntry->GetTitle(),
rName ) )
{
pEntry->SetTitle( rName );
pEntry->SetTargetURL( u""_ustr );
pEntry->SetHierarchyURL( u""_ustr );
return true;
}
}
return false;
}
bool SfxDocumentTemplates::GetFull
(
std::u16string_view rRegion, // Region Name
std::u16string_view rName, // Template Name
OUString &rPath // Out: Path + File name
)
/* [Description]
Returns Path + File name of the template specified by rRegion and rName.
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::GetLogicNames(const String&,String&,String&)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
// We don't search for empty names!
if ( rName.empty() )
return false;
if ( ! pImp->Construct() )
return false;
DocTempl_EntryData_Impl* pEntry = nullptr;
const sal_uInt16 nCount = GetRegionCount();
for ( sal_uInt16 i = 0; i < nCount; ++i )
{
RegionData_Impl *pRegion = pImp->GetRegion( i );
if( pRegion &&
( rRegion.empty() || ( rRegion == pRegion->GetTitle() ) ) )
{
pEntry = pRegion->GetEntry( rName );
if ( pEntry )
{
rPath = pEntry->GetTargetURL();
break;
}
}
}
return ( pEntry != nullptr );
}
bool SfxDocumentTemplates::GetLogicNames
(
std::u16string_view rPath, // Full Path to the template
OUString &rRegion, // Out: Region name
OUString &rName // Out: Template name
) const
/* [Description]
Returns and logical path name to the template specified by rPath
[Return value]
sal_Bool sal_True, Action could be performed
sal_False, Action could not be performed
[Cross-references]
<SfxDocumentTemplates::GetFull(const String&,const String&,DirEntry&)>
*/
{
DocTemplLocker_Impl aLocker( *pImp );
if ( ! pImp->Construct() )
return false;
INetURLObject aFullPath;
aFullPath.SetSmartProtocol( INetProtocol::File );
aFullPath.SetURL( rPath );
const OUString aPath( aFullPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
const sal_uInt16 nCount = GetRegionCount();
for ( sal_uInt16 i=0; i<nCount; ++i )
{
RegionData_Impl *pData = pImp->GetRegion( i );
if ( pData )
{
const sal_uInt16 nChildCount = pData->GetCount();
for ( sal_uInt16 j=0; j<nChildCount; ++j )
{
DocTempl_EntryData_Impl *pEntry = pData->GetEntry( j );
if ( pEntry && pEntry->GetTargetURL() == aPath )
{
rRegion = pData->GetTitle();
rName = pEntry->GetTitle();
return true;
}
}
}
}
return false;
}
SfxDocumentTemplates::SfxDocumentTemplates()
/* [Description]
Constructor
*/
{
if ( !gpTemplateData )
gpTemplateData = new SfxDocTemplate_Impl;
pImp = gpTemplateData;
}
SfxDocumentTemplates::~SfxDocumentTemplates()
/* [Description]
Destructor
Release of administrative data
*/
{
pImp = nullptr;
}
void SfxDocumentTemplates::Update( )
{
if ( ::svt::TemplateFolderCache( true ).needsUpdate() ) // update is really necessary
{
if ( pImp->Construct() )
pImp->Rescan();
}
}
void SfxDocumentTemplates::ReInitFromComponent()
{
pImp->ReInitFromComponent();
}
DocTempl_EntryData_Impl::DocTempl_EntryData_Impl( RegionData_Impl* pParent,
const OUString& rTitle )
{
mpParent = pParent;
maTitle = SfxDocumentTemplates::ConvertResourceString(rTitle);
}
int DocTempl_EntryData_Impl::Compare( std::u16string_view rTitle ) const
{
return maTitle.compareTo( rTitle );
}
const OUString& DocTempl_EntryData_Impl::GetHierarchyURL()
{
if ( maOwnURL.isEmpty() )
{
INetURLObject aTemplateObj( mpParent->GetHierarchyURL() );
aTemplateObj.insertName( GetTitle(), false,
INetURLObject::LAST_SEGMENT,
INetURLObject::EncodeMechanism::All );
maOwnURL = aTemplateObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
DBG_ASSERT( !maOwnURL.isEmpty(), "GetHierarchyURL(): Could not create URL!" );
}
return maOwnURL;
}
const OUString& DocTempl_EntryData_Impl::GetTargetURL()
{
if ( maTargetURL.isEmpty() )
{
uno::Reference< XCommandEnvironment > aCmdEnv;
Content aRegion;
if ( Content::create( GetHierarchyURL(), aCmdEnv, comphelper::getProcessComponentContext(), aRegion ) )
{
getTextProperty_Impl( aRegion, TARGET_URL, maTargetURL );
}
else
{
SAL_WARN( "sfx.doc", "GetTargetURL(): Could not create hierarchy content!" );
}
}
return maTargetURL;
}
RegionData_Impl::RegionData_Impl( const SfxDocTemplate_Impl* pParent,
OUString aTitle )
: mpParent(pParent), maTitle(std::move(aTitle))
{
}
size_t RegionData_Impl::GetEntryPos( std::u16string_view rTitle, bool& rFound ) const
{
const size_t nCount = maEntries.size();
for ( size_t i=0; i<nCount; ++i )
{
auto &pData = maEntries[ i ];
if ( pData->Compare( rTitle ) == 0 )
{
rFound = true;
return i;
}
}
rFound = false;
return nCount;
}
void RegionData_Impl::AddEntry( const OUString& rTitle,
const OUString& rTargetURL,
const size_t *pPos )
{
INetURLObject aLinkObj( GetHierarchyURL() );
aLinkObj.insertName( rTitle, false,
INetURLObject::LAST_SEGMENT,
INetURLObject::EncodeMechanism::All );
const OUString aLinkURL = aLinkObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
bool bFound = false;
size_t nPos = GetEntryPos( rTitle, bFound );
if ( bFound )
return;
if ( pPos )
nPos = *pPos;
auto pEntry = std::make_unique<DocTempl_EntryData_Impl>(
this, rTitle );
pEntry->SetTargetURL( rTargetURL );
pEntry->SetHierarchyURL( aLinkURL );
if ( nPos < maEntries.size() ) {
auto it = maEntries.begin();
std::advance( it, nPos );
maEntries.insert( it, std::move(pEntry) );
}
else
maEntries.push_back( std::move(pEntry) );
}
size_t RegionData_Impl::GetCount() const
{
return maEntries.size();
}
const OUString& RegionData_Impl::GetHierarchyURL()
{
if ( maOwnURL.isEmpty() )
{
INetURLObject aRegionObj( mpParent->GetRootURL() );
aRegionObj.insertName( GetTitle(), false,
INetURLObject::LAST_SEGMENT,
INetURLObject::EncodeMechanism::All );
maOwnURL = aRegionObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
DBG_ASSERT( !maOwnURL.isEmpty(), "GetHierarchyURL(): Could not create URL!" );
}
return maOwnURL;
}
DocTempl_EntryData_Impl* RegionData_Impl::GetEntry( std::u16string_view rName ) const
{
bool bFound = false;
tools::Long nPos = GetEntryPos( rName, bFound );
if ( bFound )
return maEntries[ nPos ].get();
return nullptr;
}
DocTempl_EntryData_Impl* RegionData_Impl::GetEntry( size_t nIndex ) const
{
if ( nIndex < maEntries.size() )
return maEntries[ nIndex ].get();
return nullptr;
}
void RegionData_Impl::DeleteEntry( size_t nIndex )
{
if ( nIndex < maEntries.size() )
{
auto it = maEntries.begin();
std::advance( it, nIndex );
maEntries.erase( it );
}
}
int RegionData_Impl::Compare( RegionData_Impl const * pCompare ) const
{
return maTitle.compareTo( pCompare->maTitle );
}
SfxDocTemplate_Impl::SfxDocTemplate_Impl()
: maStandardGroup(DocTemplLocaleHelper::GetStandardGroupString())
, mbConstructed(false)
, mnLockCounter(0)
{
}
SfxDocTemplate_Impl::~SfxDocTemplate_Impl()
{
gpTemplateData = nullptr;
}
void SfxDocTemplate_Impl::IncrementLock()
{
std::unique_lock aGuard( maMutex );
mnLockCounter++;
}
void SfxDocTemplate_Impl::DecrementLock()
{
std::unique_lock aGuard( maMutex );
if ( mnLockCounter )
mnLockCounter--;
}
RegionData_Impl* SfxDocTemplate_Impl::GetRegion( size_t nIndex ) const
{
if ( nIndex < maRegions.size() )
return maRegions[ nIndex ].get();
return nullptr;
}
RegionData_Impl* SfxDocTemplate_Impl::GetRegion( std::u16string_view rName )
const
{
for (auto& pData : maRegions)
{
if( pData->GetTitle() == rName )
return pData.get();
}
return nullptr;
}
void SfxDocTemplate_Impl::DeleteRegion( size_t nIndex )
{
if ( nIndex < maRegions.size() )
{
auto it = maRegions.begin();
std::advance( it, nIndex );
maRegions.erase( it );
}
}
/* AddRegion adds a Region to the RegionList
*/
void SfxDocTemplate_Impl::AddRegion( std::unique_lock<std::mutex>& /*rGuard*/,
const OUString& rTitle,
Content& rContent )
{
auto pRegion = std::make_unique<RegionData_Impl>( this, rTitle );
auto pRegionTmp = pRegion.get();
if ( ! InsertRegion( std::move(pRegion), size_t(-1) ) )
{
return;
}
// now get the content of the region
uno::Reference< XResultSet > xResultSet;
try
{
xResultSet = rContent.createSortedCursor( { TITLE, TARGET_URL }, { { 1, true } }, m_rCompareFactory, INCLUDE_DOCUMENTS_ONLY );
}
catch ( Exception& ) {}
if ( !xResultSet.is() )
return;
uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
try
{
while ( xResultSet->next() )
{
pRegionTmp->AddEntry( xRow->getString( 1 ), xRow->getString( 2 ), nullptr );
}
}
catch ( Exception& ) {}
}
void SfxDocTemplate_Impl::CreateFromHierarchy( std::unique_lock<std::mutex>& rGuard, Content &rTemplRoot )
{
uno::Reference< XResultSet > xResultSet;
Sequence< OUString > aProps { TITLE };
try
{
xResultSet = rTemplRoot.createSortedCursor(
aProps,
{ // Sequence
{ // NumberedSortingInfo
/* ColumnIndex */ 1, /* Ascending */ true
}
},
m_rCompareFactory,
INCLUDE_FOLDERS_ONLY
);
}
catch ( Exception& ) {}
if ( !xResultSet.is() )
return;
uno::Reference< XCommandEnvironment > aCmdEnv;
uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
try
{
while ( xResultSet->next() )
{
const OUString aId = xContentAccess->queryContentIdentifierString();
Content aContent( aId, aCmdEnv, comphelper::getProcessComponentContext() );
AddRegion( rGuard, xRow->getString( 1 ), aContent );
}
}
catch ( Exception& ) {}
}
bool SfxDocTemplate_Impl::Construct( )
{
std::unique_lock aGuard( maMutex );
if ( mbConstructed )
return true;
const uno::Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
mxInfo.set(document::DocumentProperties::create(xContext), UNO_QUERY);
mxTemplates = frame::DocumentTemplates::create(xContext);
uno::Reference< XLocalizable > xLocalizable( mxTemplates, UNO_QUERY );
m_rCompareFactory = AnyCompareFactory::createWithLocale(xContext, xLocalizable->getLocale());
uno::Reference < XContent > aRootContent = mxTemplates->getContent();
uno::Reference < XCommandEnvironment > aCmdEnv;
if ( ! aRootContent.is() )
return false;
mbConstructed = true;
maRootURL = aRootContent->getIdentifier()->getContentIdentifier();
Content aTemplRoot( aRootContent, aCmdEnv, xContext );
CreateFromHierarchy( aGuard, aTemplRoot );
return true;
}
void SfxDocTemplate_Impl::ReInitFromComponent()
{
uno::Reference< XDocumentTemplates > xTemplates = getDocTemplates();
if ( xTemplates.is() )
{
uno::Reference < XContent > aRootContent = xTemplates->getContent();
uno::Reference < XCommandEnvironment > aCmdEnv;
Content aTemplRoot( aRootContent, aCmdEnv, comphelper::getProcessComponentContext() );
Clear();
std::unique_lock aGuard(maMutex);
CreateFromHierarchy( aGuard, aTemplRoot );
}
}
bool SfxDocTemplate_Impl::InsertRegion( std::unique_ptr<RegionData_Impl> pNew, size_t nPos )
{
// return false (not inserted) if the entry already exists
for (auto const& pRegion : maRegions)
if ( pRegion->Compare( pNew.get() ) == 0 )
return false;
size_t newPos = nPos;
if ( pNew->GetTitle() == maStandardGroup )
newPos = 0;
if ( newPos < maRegions.size() )
{
auto it = maRegions.begin();
std::advance( it, newPos );
maRegions.emplace( it, std::move(pNew) );
}
else
maRegions.emplace_back( std::move(pNew) );
return true;
}
void SfxDocTemplate_Impl::Rescan()
{
Clear();
try
{
uno::Reference< XDocumentTemplates > xTemplates = getDocTemplates();
DBG_ASSERT( xTemplates.is(), "SfxDocTemplate_Impl::Rescan:invalid template instance!" );
if ( xTemplates.is() )
{
xTemplates->update();
uno::Reference < XContent > aRootContent = xTemplates->getContent();
uno::Reference < XCommandEnvironment > aCmdEnv;
Content aTemplRoot( aRootContent, aCmdEnv, comphelper::getProcessComponentContext() );
std::unique_lock aGuard(maMutex);
CreateFromHierarchy( aGuard, aTemplRoot );
}
}
catch( const Exception& )
{
TOOLS_WARN_EXCEPTION( "sfx.doc", "SfxDocTemplate_Impl::Rescan: caught an exception while doing the update" );
}
}
bool SfxDocTemplate_Impl::GetTitleFromURL( const OUString& rURL,
OUString& aTitle )
{
if ( mxInfo.is() )
{
try
{
mxInfo->read( rURL );
}
catch ( Exception& )
{
// the document is not a StarOffice document
return false;
}
try
{
uno::Reference< XPropertySet > aPropSet( mxInfo, UNO_QUERY );
if ( aPropSet.is() )
{
Any aValue = aPropSet->getPropertyValue( TITLE );
aValue >>= aTitle;
}
}
catch ( IOException& ) {}
catch ( UnknownPropertyException& ) {}
catch ( Exception& ) {}
}
if ( aTitle.isEmpty() )
{
INetURLObject aURL( rURL );
aURL.CutExtension();
aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
INetURLObject::DecodeMechanism::WithCharset );
}
return true;
}
void SfxDocTemplate_Impl::Clear()
{
std::unique_lock aGuard( maMutex );
if ( mnLockCounter )
return;
maRegions.clear();
}
bool getTextProperty_Impl( Content& rContent,
const OUString& rPropName,
OUString& rPropValue )
{
bool bGotProperty = false;
// Get the property
try
{
uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
// check, whether or not the property exists
if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
{
return false;
}
// now get the property
Any aAnyValue = rContent.getPropertyValue( rPropName );
aAnyValue >>= rPropValue;
if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
{
SfxURLRelocator_Impl aRelocImpl( ::comphelper::getProcessComponentContext() );
aRelocImpl.makeAbsoluteURL( rPropValue );
}
bGotProperty = true;
}
catch ( RuntimeException& ) {}
catch ( Exception& ) {}
return bGotProperty;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'CutExtension' is required to be utilized.
↑ V530 The return value of function 'CutExtension' is required to be utilized.