diff options
Diffstat (limited to 'sfx2/source/doc/doctempl.cxx')
-rw-r--r-- | sfx2/source/doc/doctempl.cxx | 1751 |
1 files changed, 1751 insertions, 0 deletions
diff --git a/sfx2/source/doc/doctempl.cxx b/sfx2/source/doc/doctempl.cxx new file mode 100644 index 0000000000..63d83f1298 --- /dev/null +++ b/sfx2/source/doc/doctempl.cxx @@ -0,0 +1,1751 @@ +/* -*- 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("Hidden", true) }; + + INetURLObject aTemplURL( rName ); + uno::Reference< XDocumentPropertiesSupplier > xDocPropsSupplier; + uno::Reference< XStorable > xStorable; + try + { + xStorable.set( + xDesktop->loadComponentFromURL( aTemplURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), + "_blank", + 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( 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( "" ); + 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( "" ); + pEntry->SetHierarchyURL( "" ); + 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() +: 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; + + uno::Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + + uno::Reference< XPersist > xInfo( document::DocumentProperties::create(xContext), UNO_QUERY ); + mxInfo = xInfo; + + 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(); + + maStandardGroup = DocTemplLocaleHelper::GetStandardGroupString(); + 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: */ |