/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "doctemplateslocal.hxx" #include #include #include #include #include constexpr OUStringLiteral SERVICENAME_TYPEDETECTION = u"com.sun.star.document.TypeDetection"; constexpr OUStringLiteral TEMPLATE_ROOT_URL = u"vnd.sun.star.hier:/templates"; constexpr OUString TITLE = u"Title"_ustr; constexpr OUString IS_FOLDER = u"IsFolder"_ustr; constexpr OUString IS_DOCUMENT = u"IsDocument"_ustr; constexpr OUString TARGET_URL = u"TargetURL"_ustr; constexpr OUStringLiteral TEMPLATE_VERSION = u"TemplateComponentVersion"; constexpr OUStringLiteral TEMPLATE_VERSION_VALUE = u"2"; constexpr OUStringLiteral TYPE_FOLDER = u"application/vnd.sun.star.hier-folder"; constexpr OUStringLiteral TYPE_LINK = u"application/vnd.sun.star.hier-link"; constexpr OUString TYPE_FSYS_FOLDER = u"application/vnd.sun.staroffice.fsys-folder"_ustr; constexpr OUStringLiteral TYPE_FSYS_FILE = u"application/vnd.sun.staroffice.fsys-file"; constexpr OUString PROPERTY_DIRLIST = u"DirectoryList"_ustr; constexpr OUString PROPERTY_NEEDSUPDATE = u"NeedsUpdate"_ustr; constexpr OUString PROPERTY_TYPE = u"TypeDescription"_ustr; constexpr OUString TARGET_DIR_URL = u"TargetDirURL"_ustr; constexpr OUStringLiteral COMMAND_DELETE = u"delete"; constexpr OUString STANDARD_FOLDER = u"standard"_ustr; #define C_DELIM ';' using namespace ::com::sun::star; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::document; using namespace ::com::sun::star::io; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::sdbc; using namespace ::com::sun::star::ucb; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::container; using namespace ::com::sun::star::util; using namespace ::ucbhelper; using namespace ::comphelper; using ::std::vector; namespace { class WaitWindow_Impl : public WorkWindow { tools::Rectangle maRect; OUString maText; static constexpr DrawTextFlags gnTextStyle = DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::WordBreak | DrawTextFlags::MultiLine; public: WaitWindow_Impl(); virtual ~WaitWindow_Impl() override; virtual void dispose() override; virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; }; #define X_OFFSET 15 #define Y_OFFSET 15 struct NamePair_Impl { OUString maShortName; OUString maLongName; }; class DocTemplates_EntryData_Impl; class GroupData_Impl; typedef vector< std::unique_ptr > GroupList_Impl; class TplTaskEnvironment : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment > { uno::Reference< task::XInteractionHandler > m_xInteractionHandler; public: explicit TplTaskEnvironment( uno::Reference< task::XInteractionHandler> xInteractionHandler ) : m_xInteractionHandler(std::move( xInteractionHandler )) {} virtual uno::Reference SAL_CALL getInteractionHandler() override { return m_xInteractionHandler; } virtual uno::Reference SAL_CALL getProgressHandler() override { return uno::Reference(); } }; class SfxDocTplService : public ::cppu::WeakImplHelper< css::lang::XLocalizable, css::frame::XDocumentTemplates, css::lang::XServiceInfo > { public: explicit SfxDocTplService( const css::uno::Reference < uno::XComponentContext >& xContext ); virtual ~SfxDocTplService() override; virtual OUString SAL_CALL getImplementationName() override { return u"com.sun.star.comp.sfx2.DocumentTemplates"_ustr; } virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override { return cppu::supportsService(this, ServiceName); } virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override { css::uno::Sequence< OUString > aSeq { u"com.sun.star.frame.DocumentTemplates"_ustr }; return aSeq; } // --- XLocalizable --- void SAL_CALL setLocale( const css::lang::Locale & eLocale ) override; css::lang::Locale SAL_CALL getLocale() override; // --- XDocumentTemplates --- css::uno::Reference< css::ucb::XContent > SAL_CALL getContent() override; sal_Bool SAL_CALL storeTemplate( const OUString& GroupName, const OUString& TemplateName, const css::uno::Reference< css::frame::XStorable >& Storable ) override; sal_Bool SAL_CALL addTemplate( const OUString& GroupName, const OUString& TemplateName, const OUString& SourceURL ) override; sal_Bool SAL_CALL removeTemplate( const OUString& GroupName, const OUString& TemplateName ) override; sal_Bool SAL_CALL renameTemplate( const OUString& GroupName, const OUString& OldTemplateName, const OUString& NewTemplateName ) override; sal_Bool SAL_CALL addGroup( const OUString& GroupName ) override; sal_Bool SAL_CALL removeGroup( const OUString& GroupName ) override; sal_Bool SAL_CALL renameGroup( const OUString& OldGroupName, const OUString& NewGroupName ) override; void SAL_CALL update() override; private: bool init() { if ( !mbIsInitialized ) init_Impl(); return mbIsInitialized; } void doUpdate(); uno::Reference< XComponentContext > mxContext; rtl::Reference< TplTaskEnvironment > maCmdEnv; uno::Reference< XDocumentProperties> m_xDocProps; uno::Reference< XTypeDetection > mxType; ::osl::Mutex maMutex; Sequence< OUString > maTemplateDirs; Sequence< OUString > maInternalTemplateDirs; OUString maRootURL; std::vector< NamePair_Impl > maNames; lang::Locale maLocale; Content maRootContent; bool mbIsInitialized : 1; bool mbLocaleSet : 1; SfxURLRelocator_Impl maRelocator; void init_Impl(); void getDefaultLocale(); void getDirList(); void readFolderList(); bool needsUpdate(); OUString getLongName( const OUString& rShortName ); bool setTitleForURL( const OUString& rURL, const OUString& aTitle ); void getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, bool& bDocHasTitle ); bool addEntry( Content& rParentFolder, const OUString& rTitle, const OUString& rTargetURL, const OUString& rType ); bool createFolder( const OUString& rNewFolderURL, bool bCreateParent, bool bFsysFolder, Content &rNewFolder ); static bool CreateNewUniqueFolderWithPrefix( std::u16string_view aPath, const OUString& aPrefix, OUString& aNewFolderName, OUString& aNewFolderURL, Content& aNewFolder ); static OUString CreateNewUniqueFileWithPrefix( std::u16string_view aPath, const OUString& aPrefix, std::u16string_view aExt ); std::vector< beans::StringPair > ReadUINamesForTemplateDir_Impl( std::u16string_view aUserPath ); bool UpdateUINamesForTemplateDir_Impl( std::u16string_view aUserPath, const OUString& aGroupName, const OUString& aNewFolderName ); bool ReplaceUINamesForTemplateDir_Impl( std::u16string_view aUserPath, const OUString& aFsysGroupName, std::u16string_view aOldGroupName, const OUString& aNewGroupName ); void RemoveUINamesForTemplateDir_Impl( std::u16string_view aUserPath, std::u16string_view aGroupName ); bool WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath, const std::vector< beans::StringPair >& aUINames ); OUString CreateNewGroupFsys( const OUString& rGroupName, Content& aGroup ); static bool removeContent( Content& rContent ); bool removeContent( const OUString& rContentURL ); bool setProperty( Content& rContent, const OUString& rPropName, const Any& rPropValue ); bool getProperty( Content& rContent, const OUString& rPropName, Any& rPropValue ); void createFromContent( GroupList_Impl& rList, Content &rContent, bool bHierarchy, bool bWriteableContent ); void addHierGroup( GroupList_Impl& rList, const OUString& rTitle, const OUString& rOwnURL ); void addFsysGroup( GroupList_Impl& rList, const OUString& rTitle, const OUString& rUITitle, const OUString& rOwnURL, bool bWriteableGroup ); void removeFromHierarchy( DocTemplates_EntryData_Impl const *pData ); void addToHierarchy( GroupData_Impl const *pGroup, DocTemplates_EntryData_Impl const *pData ); void removeFromHierarchy( GroupData_Impl const *pGroup ); void addGroupToHierarchy( GroupData_Impl *pGroup ); void updateData( DocTemplates_EntryData_Impl const *pData ); //See: #i66157# and rhbz#1065807 //return which template dir the rURL is a subpath of OUString findParentTemplateDir(const OUString& rURL) const; //See: #i66157# and rhbz#1065807 //return true if rURL is a path (or subpath of) a dir which is not a user path //which implies neither it or its contents can be removed bool isInternalTemplateDir(const OUString& rURL) const; }; class DocTemplates_EntryData_Impl { OUString maTitle; OUString maType; OUString maTargetURL; OUString maHierarchyURL; bool mbInHierarchy : 1; bool mbInUse : 1; bool mbUpdateType : 1; bool mbUpdateLink : 1; public: explicit DocTemplates_EntryData_Impl( OUString aTitle ); void setInUse() { mbInUse = true; } void setHierarchy( bool bInHierarchy ) { mbInHierarchy = bInHierarchy; } void setUpdateLink( bool bUpdateLink ) { mbUpdateLink = bUpdateLink; } void setUpdateType( bool bUpdateType ) { mbUpdateType = bUpdateType; } bool getInUse() const { return mbInUse; } bool getInHierarchy() const { return mbInHierarchy; } bool getUpdateLink() const { return mbUpdateLink; } bool getUpdateType() const { return mbUpdateType; } const OUString& getHierarchyURL() const { return maHierarchyURL; } const OUString& getTargetURL() const { return maTargetURL; } const OUString& getTitle() const { return maTitle; } const OUString& getType() const { return maType; } void setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; } void setTargetURL( const OUString& rURL ) { maTargetURL = rURL; } void setType( const OUString& rType ) { maType = rType; } }; class GroupData_Impl { std::vector< std::unique_ptr > maEntries; OUString maTitle; OUString maHierarchyURL; OUString maTargetURL; bool mbInUse : 1; bool mbInHierarchy : 1; public: explicit GroupData_Impl( OUString aTitle ); void setInUse() { mbInUse = true; } void setHierarchy( bool bInHierarchy ) { mbInHierarchy = bInHierarchy; } void setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; } void setTargetURL( const OUString& rURL ) { maTargetURL = rURL; } bool getInUse() const { return mbInUse; } bool getInHierarchy() const { return mbInHierarchy; } const OUString& getHierarchyURL() const { return maHierarchyURL; } const OUString& getTargetURL() const { return maTargetURL; } const OUString& getTitle() const { return maTitle; } DocTemplates_EntryData_Impl* addEntry( const OUString& rTitle, const OUString& rTargetURL, const OUString& rType, const OUString& rHierURL ); size_t count() { return maEntries.size(); } DocTemplates_EntryData_Impl* getEntry( size_t nPos ) { return maEntries[ nPos ].get(); } }; // private SfxDocTplService_Impl void SfxDocTplService::init_Impl() { const uno::Reference< uno::XComponentContext >& xContext = ::comphelper::getProcessComponentContext(); uno::Reference < task::XInteractionHandler > xInteractionHandler( task::InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW ); maCmdEnv = new TplTaskEnvironment( xInteractionHandler ); ::osl::ClearableMutexGuard aGuard( maMutex ); bool bIsInitialized = false; bool bNeedsUpdate = false; if ( !mbLocaleSet ) getDefaultLocale(); // convert locale to string // set maRootContent to the root of the templates hierarchy. Create the // entry if necessary maRootURL = TEMPLATE_ROOT_URL + "/" + LanguageTag::convertToBcp47(maLocale); const OUString aTemplVersPropName( TEMPLATE_VERSION ); const OUString aTemplVers( TEMPLATE_VERSION_VALUE ); if ( Content::create( maRootURL, maCmdEnv, comphelper::getProcessComponentContext(), maRootContent ) ) { uno::Any aValue; OUString aPropValue; if ( getProperty( maRootContent, aTemplVersPropName, aValue ) && ( aValue >>= aPropValue ) && aPropValue == aTemplVers ) { bIsInitialized = true; } else removeContent( maRootContent ); } if ( !bIsInitialized ) { if ( createFolder( maRootURL, true, false, maRootContent ) && setProperty( maRootContent, aTemplVersPropName, uno::Any( aTemplVers ) ) ) bIsInitialized = true; bNeedsUpdate = true; } if ( bIsInitialized ) { try { m_xDocProps.set(document::DocumentProperties::create( ::comphelper::getProcessComponentContext())); } catch (uno::RuntimeException const&) { TOOLS_WARN_EXCEPTION("sfx.doc", "SfxDocTplService_Impl::init_Impl: cannot create DocumentProperties service:"); } mxType.set( mxContext->getServiceManager()->createInstanceWithContext(SERVICENAME_TYPEDETECTION, mxContext), UNO_QUERY ); getDirList(); readFolderList(); if ( bNeedsUpdate ) { aGuard.clear(); SolarMutexClearableGuard aSolarGuard; VclPtrInstance< WaitWindow_Impl > pWin; aSolarGuard.clear(); { osl::MutexGuard anotherGuard(maMutex); doUpdate(); } SolarMutexGuard aSecondSolarGuard; pWin.disposeAndClear(); } else if ( needsUpdate() ) // the UI should be shown only on the first update doUpdate(); } else { SAL_WARN( "sfx.doc", "init_Impl(): Could not create root" ); } mbIsInitialized = bIsInitialized; } void SfxDocTplService::getDefaultLocale() { if ( !mbLocaleSet ) { ::osl::MutexGuard aGuard( maMutex ); if ( !mbLocaleSet ) { maLocale = LanguageTag::convertToLocale( utl::ConfigManager::getUILocale(), false); mbLocaleSet = true; } } } const char* TEMPLATE_SHORT_NAMES_ARY[] = { "standard", "styles", "officorr", "offimisc", "personal", "presnt", "draw", "l10n", }; void SfxDocTplService::readFolderList() { SolarMutexGuard aGuard; static_assert( SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY) == SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY), "mismatch array lengths" ); const size_t nCount = std::min(SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY), SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY)); for (size_t i = 0; i < nCount; ++i) { NamePair_Impl aPair; aPair.maShortName = OUString::createFromAscii(TEMPLATE_SHORT_NAMES_ARY[i]); aPair.maLongName = SfxResId(TEMPLATE_LONG_NAMES_ARY[i]); maNames.push_back( aPair ); } } OUString SfxDocTplService::getLongName( const OUString& rShortName ) { OUString aRet; for (auto const & rPair : maNames) { if ( rPair.maShortName == rShortName ) { aRet = rPair.maLongName; break; } } if ( aRet.isEmpty() ) aRet = rShortName; return aRet; } void SfxDocTplService::getDirList() { Any aValue; // Get the template dir list // TODO/LATER: let use service, register listener INetURLObject aURL; OUString aDirs = SvtPathOptions().GetTemplatePath(); sal_Int32 nCount = comphelper::string::getTokenCount(aDirs, C_DELIM); maTemplateDirs = Sequence< OUString >( nCount ); uno::Reference< util::XMacroExpander > xExpander = util::theMacroExpander::get(mxContext); sal_Int32 nIdx{ 0 }; for (auto& rTemplateDir : asNonConstRange(maTemplateDirs)) { aURL.SetSmartProtocol( INetProtocol::File ); aURL.SetURL( o3tl::getToken(aDirs, 0, C_DELIM, nIdx ) ); rTemplateDir = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if (xExpander && rTemplateDir.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &rTemplateDir)) { rTemplateDir = rtl::Uri::decode(rTemplateDir, rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8); rTemplateDir = xExpander->expandMacros( rTemplateDir ); } } aValue <<= maTemplateDirs; css::uno::Reference< css::util::XPathSettings > xPathSettings = css::util::thePathSettings::get(mxContext); // load internal paths Any aAny = xPathSettings->getPropertyValue( u"Template_internal"_ustr ); aAny >>= maInternalTemplateDirs; for (auto& rInternalTemplateDir : asNonConstRange(maInternalTemplateDirs)) { //expand vnd.sun.star.expand: and remove "..." from them //to normalize into the expected url patterns maRelocator.makeRelocatableURL(rInternalTemplateDir); maRelocator.makeAbsoluteURL(rInternalTemplateDir); } // Store the template dir list setProperty( maRootContent, PROPERTY_DIRLIST, aValue ); } bool SfxDocTplService::needsUpdate() { bool bNeedsUpdate = true; Any aValue; // Get the template dir list bool bHasProperty = getProperty( maRootContent, PROPERTY_NEEDSUPDATE, aValue ); if ( bHasProperty ) aValue >>= bNeedsUpdate; // the old template component also checks this state, but it is initialized from this component // so if this component was already updated the old component does not need such an update ::svt::TemplateFolderCache aTempCache; if ( !bNeedsUpdate ) bNeedsUpdate = aTempCache.needsUpdate(); if ( bNeedsUpdate ) aTempCache.storeState(); return bNeedsUpdate; } bool SfxDocTplService::setTitleForURL( const OUString& rURL, const OUString& aTitle ) { if (m_xDocProps.is()) { try { m_xDocProps->loadFromMedium(rURL, Sequence()); m_xDocProps->setTitle(aTitle); uno::Reference< embed::XStorage > xStorage = ::comphelper::OStorageHelper::GetStorageFromURL( rURL, embed::ElementModes::READWRITE); uno::Sequence medium( comphelper::InitPropertySequence({ { "DocumentBaseURL", Any(rURL) }, { "URL", Any(rURL) } })); m_xDocProps->storeToStorage(xStorage, medium); return true; } catch ( Exception& ) { } } return false; } void SfxDocTplService::getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, bool& bDocHasTitle ) { bDocHasTitle = false; if (m_xDocProps.is()) { try { m_xDocProps->loadFromMedium(rURL, Sequence()); aTitle = m_xDocProps->getTitle(); } catch ( Exception& ) { } } if ( aType.isEmpty() && mxType.is() ) { const OUString aDocType {mxType->queryTypeByURL( rURL )}; if ( !aDocType.isEmpty() ) try { uno::Reference< container::XNameAccess > xTypeDetection( mxType, uno::UNO_QUERY ); if (xTypeDetection) { SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aDocType ) ); aType = aTypeProps.getUnpackedValueOrDefault( u"MediaType"_ustr, OUString() ); } } catch( uno::Exception& ) {} } if ( aTitle.isEmpty() ) { INetURLObject aURL( rURL ); aURL.CutExtension(); aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); } else bDocHasTitle = true; } bool SfxDocTplService::addEntry( Content& rParentFolder, const OUString& rTitle, const OUString& rTargetURL, const OUString& rType ) { bool bAddedEntry = false; INetURLObject aLinkObj( rParentFolder.getURL() ); aLinkObj.insertName( rTitle, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aLinkURL {aLinkObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; Content aLink; if ( ! Content::create( aLinkURL, maCmdEnv, comphelper::getProcessComponentContext(), aLink ) ) { Sequence< Any > aValues{ Any(rTitle), Any(false), Any(rTargetURL) }; try { rParentFolder.insertNewContent( TYPE_LINK, { TITLE, IS_FOLDER, TARGET_URL }, aValues, aLink ); setProperty( aLink, PROPERTY_TYPE, Any( rType ) ); bAddedEntry = true; } catch( Exception& ) {} } return bAddedEntry; } bool SfxDocTplService::createFolder( const OUString& rNewFolderURL, bool bCreateParent, bool bFsysFolder, Content &rNewFolder ) { Content aParent; bool bCreatedFolder = false; INetURLObject aParentURL( rNewFolderURL ); const OUString aFolderName {aParentURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )}; // compute the parent folder url from the new folder url // and remove the final slash, because Content::create doesn't // like it aParentURL.removeSegment(); if ( aParentURL.getSegmentCount() >= 1 ) aParentURL.removeFinalSlash(); // if the parent exists, we can continue with the creation of the // new folder, we have to create the parent otherwise ( as long as // bCreateParent is set to true ) if ( Content::create( aParentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), maCmdEnv, comphelper::getProcessComponentContext(), aParent ) ) { try { Sequence< Any > aValues{ Any(aFolderName), Any(true) }; OUString aType; if ( bFsysFolder ) aType = TYPE_FSYS_FOLDER; else aType = TYPE_FOLDER; aParent.insertNewContent( aType, { TITLE, IS_FOLDER }, aValues, rNewFolder ); bCreatedFolder = true; } catch( Exception const & ) { TOOLS_WARN_EXCEPTION( "sfx.doc", "createFolder(): Could not create new folder" ); } } else if ( bCreateParent ) { // if the parent doesn't exists and bCreateParent is set to true, // we try to create the parent and if this was successful, we // try to create the new folder again ( but this time, we set // bCreateParent to false to avoid endless recursions ) if ( ( aParentURL.getSegmentCount() >= 1 ) && createFolder( aParentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), bCreateParent, bFsysFolder, aParent ) ) { bCreatedFolder = createFolder( rNewFolderURL, false, bFsysFolder, rNewFolder ); } } return bCreatedFolder; } bool SfxDocTplService::CreateNewUniqueFolderWithPrefix( std::u16string_view aPath, const OUString& aPrefix, OUString& aNewFolderName, OUString& aNewFolderURL, Content& aNewFolder ) { bool bCreated = false; INetURLObject aDirPath( aPath ); Content aParent; uno::Reference< XCommandEnvironment > aQuietEnv; if ( Content::create( aDirPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aQuietEnv, comphelper::getProcessComponentContext(), aParent ) ) { for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ ) { OUString aTryName = aPrefix; if ( nInd ) aTryName += OUString::number( nInd ); try { Sequence< Any > aValues{ Any(aTryName), Any(true) }; bCreated = aParent.insertNewContent( TYPE_FSYS_FOLDER, { TITLE, IS_FOLDER }, aValues, aNewFolder ); } catch( ucb::NameClashException& ) { // if there is already an element, retry } catch( Exception& ) { INetURLObject aObjPath( aDirPath ); aObjPath.insertName( aTryName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); // if there is already an element, retry // if there was another error, do not try any more if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ) break; } if ( bCreated ) { aNewFolderName = aTryName; aNewFolderURL = aNewFolder.get()->getIdentifier()->getContentIdentifier(); break; } } } return bCreated; } OUString SfxDocTplService::CreateNewUniqueFileWithPrefix( std::u16string_view aPath, const OUString& aPrefix, std::u16string_view aExt ) { OUString aNewFileURL; INetURLObject aDirPath( aPath ); Content aParent; uno::Reference< XCommandEnvironment > aQuietEnv; if ( Content::create( aDirPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aQuietEnv, comphelper::getProcessComponentContext(), aParent ) ) { for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ ) { Content aNewFile; bool bCreated = false; OUString aTryName = aPrefix; if ( nInd ) aTryName += OUString::number( nInd ); if ( aExt.empty() || aExt[0] != '.' ) aTryName += "."; aTryName += aExt; try { Sequence< Any > aValues{ Any(aTryName), Any(true) }; bCreated = aParent.insertNewContent( TYPE_FSYS_FILE, { TITLE, IS_DOCUMENT }, aValues, aNewFile ); } catch( ucb::NameClashException& ) { // if there is already an element, retry } catch( Exception& ) { INetURLObject aObjPath( aPath ); aObjPath.insertName( aTryName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); // if there is already an element, retry // if there was another error, do not try any more if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) ) break; } if ( bCreated ) { aNewFileURL = aNewFile.get()->getIdentifier()->getContentIdentifier(); break; } } } return aNewFileURL; } bool SfxDocTplService::removeContent( Content& rContent ) { bool bRemoved = false; try { Any aArg( true ); rContent.executeCommand( COMMAND_DELETE, aArg ); bRemoved = true; } catch ( RuntimeException& ) {} catch ( Exception& ) {} return bRemoved; } bool SfxDocTplService::removeContent( const OUString& rContentURL ) { Content aContent; if ( Content::create( rContentURL, maCmdEnv, comphelper::getProcessComponentContext(), aContent ) ) return removeContent( aContent ); return false; } bool SfxDocTplService::setProperty( Content& rContent, const OUString& rPropName, const Any& rPropValue ) { bool bPropertySet = false; // Store the property try { Any aPropValue( rPropValue ); uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties(); // check, whether or not the property exists, create it, when not if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) ) { uno::Reference< XPropertyContainer > xProperties( rContent.get(), UNO_QUERY ); if ( xProperties.is() ) { try { xProperties->addProperty( rPropName, PropertyAttribute::MAYBEVOID, rPropValue ); } catch( PropertyExistException& ) {} catch( IllegalTypeException& ) { TOOLS_WARN_EXCEPTION( "sfx.doc", "" ); } catch( IllegalArgumentException& ) { TOOLS_WARN_EXCEPTION( "sfx.doc", "" ); } } } // To ensure a reloctable office installation, the path to the // office installation directory must never be stored directly. if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) ) { OUString aValue; if ( rPropValue >>= aValue ) { maRelocator.makeRelocatableURL( aValue ); aPropValue <<= aValue; } else { Sequence< OUString > aValues; if ( rPropValue >>= aValues ) { for ( auto& rValue : asNonConstRange(aValues) ) { maRelocator.makeRelocatableURL( rValue ); } aPropValue <<= aValues; } else { OSL_FAIL( "Unsupported property value type" ); } } } // now set the property rContent.setPropertyValue( rPropName, aPropValue ); bPropertySet = true; } catch ( RuntimeException& ) {} catch ( Exception& ) {} return bPropertySet; } bool SfxDocTplService::getProperty(Content& rContent, const OUString& rPropName, Any& 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 rPropValue = rContent.getPropertyValue( rPropName ); // To ensure a reloctable office installation, the path to the // office installation directory must never be stored directly. if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) ) { OUString aValue; if ( rPropValue >>= aValue ) { maRelocator.makeAbsoluteURL( aValue ); rPropValue <<= aValue; } else { Sequence< OUString > aValues; if ( rPropValue >>= aValues ) { for ( auto& rValue : asNonConstRange(aValues) ) { maRelocator.makeAbsoluteURL( rValue ); } rPropValue <<= aValues; } else { OSL_FAIL( "Unsupported property value type" ); } } } bGotProperty = true; } catch ( RuntimeException& ) {} catch ( Exception& ) {} return bGotProperty; } SfxDocTplService::SfxDocTplService( const uno::Reference< XComponentContext > & xContext ) : mxContext(xContext), mbIsInitialized(false), mbLocaleSet(false), maRelocator(xContext) { } SfxDocTplService::~SfxDocTplService() { ::osl::MutexGuard aGuard( maMutex ); maNames.clear(); } lang::Locale SfxDocTplService::getLocale() { ::osl::MutexGuard aGuard( maMutex ); if ( !mbLocaleSet ) getDefaultLocale(); return maLocale; } void SfxDocTplService::setLocale( const lang::Locale &rLocale ) { ::osl::MutexGuard aGuard( maMutex ); if ( mbLocaleSet && ( ( maLocale.Language != rLocale.Language ) || ( maLocale.Country != rLocale.Country ) || ( maLocale.Variant != rLocale.Variant ) ) ) mbIsInitialized = false; maLocale = rLocale; mbLocaleSet = true; } void SfxDocTplService::update() { if (!init()) return; doUpdate(); } void SfxDocTplService::doUpdate() { ::osl::MutexGuard aGuard( maMutex ); const OUString aPropName( PROPERTY_NEEDSUPDATE ); Any aValue; aValue <<= true; setProperty( maRootContent, aPropName, aValue ); GroupList_Impl aGroupList; // get the entries from the hierarchy createFromContent( aGroupList, maRootContent, true, false ); // get the entries from the template directories sal_Int32 nCountDir = maTemplateDirs.getLength(); const OUString* pDirs = maTemplateDirs.getConstArray(); Content aDirContent; // the last directory in the list must be writable bool bWriteableDirectory = true; // the target folder might not exist, for this reason no interaction handler should be used uno::Reference< XCommandEnvironment > aQuietEnv; while ( nCountDir ) { nCountDir--; if ( Content::create( pDirs[ nCountDir ], aQuietEnv, comphelper::getProcessComponentContext(), aDirContent ) ) { createFromContent( aGroupList, aDirContent, false, bWriteableDirectory ); } bWriteableDirectory = false; } // now check the list for(std::unique_ptr& pGroup : aGroupList) { if ( pGroup->getInUse() ) { if ( pGroup->getInHierarchy() ) { Content aGroup; if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) setProperty( aGroup, TARGET_DIR_URL, Any( pGroup->getTargetURL() ) ); size_t nCount = pGroup->count(); for ( size_t i=0; igetEntry( i ); if ( ! pData->getInUse() ) { if ( pData->getInHierarchy() ) removeFromHierarchy( pData ); // delete entry in hierarchy else addToHierarchy( pGroup.get(), pData ); // add entry to hierarchy } else if ( pData->getUpdateType() || pData->getUpdateLink() ) { updateData( pData ); } } } else { addGroupToHierarchy( pGroup.get() ); // add group to hierarchy } } else removeFromHierarchy( pGroup.get() ); // delete group from hierarchy } aGroupList.clear(); aValue <<= false; setProperty( maRootContent, aPropName, aValue ); } std::vector< beans::StringPair > SfxDocTplService::ReadUINamesForTemplateDir_Impl( std::u16string_view aUserPath ) { INetURLObject aLocObj( aUserPath ); aLocObj.insertName( u"groupuinames.xml", false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); Content aLocContent; // TODO/LATER: Use hashmap in future std::vector< beans::StringPair > aUINames; if ( Content::create( aLocObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference < ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext(), aLocContent ) ) { try { uno::Reference< io::XInputStream > xLocStream = aLocContent.openStream(); if ( xLocStream.is() ) aUINames = DocTemplLocaleHelper::ReadGroupLocalizationSequence( xLocStream, mxContext ); } catch( uno::Exception& ) {} } return aUINames; } bool SfxDocTplService::UpdateUINamesForTemplateDir_Impl( std::u16string_view aUserPath, const OUString& aGroupName, const OUString& aNewFolderName ) { std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath ); sal_Int32 nLen = aUINames.size(); // it is possible that the name is used already, but it should be checked before for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ ) if ( aUINames[nInd].First == aNewFolderName ) return false; aUINames.resize( ++nLen ); aUINames[nLen-1].First = aNewFolderName; aUINames[nLen-1].Second = aGroupName; return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames ); } bool SfxDocTplService::ReplaceUINamesForTemplateDir_Impl( std::u16string_view aUserPath, const OUString& aDefaultFsysGroupName, std::u16string_view aOldGroupName, const OUString& aNewGroupName ) { std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath ); sal_Int32 nLen = aUINames.size(); bool bChanged = false; for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ ) if ( aUINames[nInd].Second == aOldGroupName ) { aUINames[nInd].Second = aNewGroupName; bChanged = true; } if ( !bChanged ) { aUINames.resize( ++nLen ); aUINames[nLen-1].First = aDefaultFsysGroupName; aUINames[nLen-1].Second = aNewGroupName; } return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames ); } void SfxDocTplService::RemoveUINamesForTemplateDir_Impl( std::u16string_view aUserPath, std::u16string_view aGroupName ) { std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath ); sal_Int32 nLen = aUINames.size(); std::vector< beans::StringPair > aNewUINames( nLen ); sal_Int32 nNewLen = 0; bool bChanged = false; for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ ) if ( aUINames[nInd].Second == aGroupName ) bChanged = true; else { nNewLen++; aNewUINames[nNewLen-1].First = aUINames[nInd].First; aNewUINames[nNewLen-1].Second = aUINames[nInd].Second; } aNewUINames.resize( nNewLen ); if (bChanged) WriteUINamesForTemplateDir_Impl( aUserPath, aNewUINames ); } bool SfxDocTplService::WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath, const std::vector< beans::StringPair >& aUINames ) { bool bResult = false; try { uno::Reference< io::XTempFile > xTempFile( io::TempFile::create(mxContext), uno::UNO_SET_THROW ); uno::Reference< io::XOutputStream > xOutStream = xTempFile->getOutputStream(); if ( !xOutStream.is() ) throw uno::RuntimeException(); DocTemplLocaleHelper::WriteGroupLocalizationSequence( xOutStream, aUINames, mxContext); try { // the SAX writer might close the stream xOutStream->closeOutput(); } catch( uno::Exception& ) {} Content aTargetContent( OUString(aUserPath), maCmdEnv, comphelper::getProcessComponentContext() ); Content aSourceContent( xTempFile->getUri(), maCmdEnv, comphelper::getProcessComponentContext() ); aTargetContent.transferContent( aSourceContent, InsertOperation::Copy, u"groupuinames.xml"_ustr, ucb::NameClash::OVERWRITE, u"text/xml"_ustr ); bResult = true; } catch ( uno::Exception& ) { TOOLS_WARN_EXCEPTION("sfx.doc", ""); } return bResult; } OUString SfxDocTplService::CreateNewGroupFsys( const OUString& rGroupName, Content& aGroup ) { OUString aResultURL; if ( maTemplateDirs.hasElements() ) { OUString aTargetPath = maTemplateDirs[ maTemplateDirs.getLength() - 1 ]; // create a new folder with the given name Content aNewFolder; OUString aNewFolderName; // the Fsys name instead of GroupName should be used, the groupuinames must be added also if ( !CreateNewUniqueFolderWithPrefix( aTargetPath, rGroupName, aNewFolderName, aResultURL, aNewFolder ) && !CreateNewUniqueFolderWithPrefix( aTargetPath, u"UserGroup"_ustr, aNewFolderName, aResultURL, aNewFolder ) ) return OUString(); if ( !UpdateUINamesForTemplateDir_Impl( aTargetPath, rGroupName, aNewFolderName ) ) { // we could not create the groupuinames for the folder, so we delete the group in // the folder and return removeContent( aNewFolder ); return OUString(); } // Now set the target url for this group and we are done Any aValue( aResultURL ); if ( ! setProperty( aGroup, TARGET_DIR_URL, aValue ) ) { removeContent( aNewFolder ); return OUString(); } } return aResultURL; } sal_Bool SfxDocTplService::addGroup( const OUString& rGroupName ) { if (!init()) return false; ::osl::MutexGuard aGuard( maMutex ); // Check, whether or not there is a group with this name Content aNewGroup; OUString aNewGroupURL; INetURLObject aNewGroupObj( maRootURL ); aNewGroupObj.insertName( rGroupName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); aNewGroupURL = aNewGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if ( Content::create( aNewGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aNewGroup ) || ! createFolder( aNewGroupURL, false, false, aNewGroup ) ) { // if there already was a group with this name or the new group // could not be created, we return here return false; } // Get the user template path entry ( new group will always // be added in the user template path ) sal_Int32 nIndex; OUString aUserPath; nIndex = maTemplateDirs.getLength(); if ( nIndex ) nIndex--; else return false; // We don't know where to add the group aUserPath = maTemplateDirs[ nIndex ]; // create a new folder with the given name Content aNewFolder; OUString aNewFolderName; OUString aNewFolderURL; // the Fsys name instead of GroupName should be used, the groupuinames must be added also if ( !CreateNewUniqueFolderWithPrefix( aUserPath, rGroupName, aNewFolderName, aNewFolderURL, aNewFolder ) && !CreateNewUniqueFolderWithPrefix( aUserPath, u"UserGroup"_ustr, aNewFolderName, aNewFolderURL, aNewFolder ) ) { // we could not create the folder, so we delete the group in the // hierarchy and return removeContent( aNewGroup ); return false; } if ( !UpdateUINamesForTemplateDir_Impl( aUserPath, rGroupName, aNewFolderName ) ) { // we could not create the groupuinames for the folder, so we delete the group in the // hierarchy, the folder and return removeContent( aNewGroup ); removeContent( aNewFolder ); return false; } // Now set the target url for this group and we are done Any aValue( aNewFolderURL ); if ( ! setProperty( aNewGroup, TARGET_DIR_URL, aValue ) ) { removeContent( aNewGroup ); removeContent( aNewFolder ); return false; } return true; } sal_Bool SfxDocTplService::removeGroup( const OUString& rGroupName ) { // remove all the elements that have the prefix aTargetURL // if the group does not have other elements remove it if (!init()) return false; ::osl::MutexGuard aGuard( maMutex ); bool bResult = false; // create the group url INetURLObject aGroupObj( maRootURL ); aGroupObj.insertName( rGroupName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); // Get the target url Content aGroup; const OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if ( Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) { const OUString aPropName( TARGET_DIR_URL ); Any aValue; OUString aGroupTargetURL; if ( getProperty( aGroup, aPropName, aValue ) ) aValue >>= aGroupTargetURL; if ( aGroupTargetURL.isEmpty() ) return false; // nothing is allowed to be removed if ( !maTemplateDirs.hasElements() ) return false; // check that the fs location is in writable folder and this is not a "My templates" folder INetURLObject aGroupParentFolder( aGroupTargetURL ); if (!aGroupParentFolder.removeSegment()) return false; OUString aGeneralTempPath = findParentTemplateDir( aGroupParentFolder.GetMainURL(INetURLObject::DecodeMechanism::NONE)); if (aGeneralTempPath.isEmpty()) return false; // now get the content of the Group uno::Reference< XResultSet > xResultSet; Sequence< OUString > aProps { TARGET_URL }; try { xResultSet = aGroup.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY ); if ( xResultSet.is() ) { bool bHasNonRemovable = false; bool bHasShared = false; uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW ); uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW ); while ( xResultSet->next() ) { OUString aTemplTargetURL( xRow->getString( 1 ) ); OUString aHierURL = xContentAccess->queryContentIdentifierString(); if ( ::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, aTemplTargetURL ) ) { // this is a user template, and it can be removed if ( removeContent( aTemplTargetURL ) ) removeContent( aHierURL ); else bHasNonRemovable = true; } else bHasShared = true; } if ( !bHasNonRemovable && !bHasShared ) { if ( removeContent( aGroupTargetURL ) || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) ) { removeContent( aGroupURL ); RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName ); bResult = true; // the operation is successful only if the whole group is removed } } else if ( !bHasNonRemovable ) { if ( removeContent( aGroupTargetURL ) || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) ) { RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName ); setProperty( aGroup, aPropName, uno::Any( OUString() ) ); } } } } catch ( Exception& ) {} } return bResult; } sal_Bool SfxDocTplService::renameGroup( const OUString& rOldName, const OUString& rNewName ) { if ( rOldName == rNewName ) return true; if (!init()) return false; ::osl::MutexGuard aGuard( maMutex ); // create the group url Content aGroup; INetURLObject aGroupObj( maRootURL ); aGroupObj.insertName( rNewName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); // Check, if there is a group with the new name, return false // if there is one. if ( Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) return false; aGroupObj.removeSegment(); aGroupObj.insertName( rOldName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); // When there is no group with the old name, we can't rename it if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) return false; OUString aGroupTargetURL; // there is no need to check whether target dir url is in target path, since if the target path is changed // the target dir url should be already generated new Any aValue; if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) ) aValue >>= aGroupTargetURL; if ( aGroupTargetURL.isEmpty() ) return false; if ( !maTemplateDirs.hasElements() ) return false; // check that the fs location is in writable folder and this is not a "My templates" folder INetURLObject aGroupParentFolder( aGroupTargetURL ); if (!aGroupParentFolder.removeSegment() || isInternalTemplateDir(aGroupParentFolder.GetMainURL(INetURLObject::DecodeMechanism::NONE))) { return false; } // check that the group can be renamed ( all the contents must be in target location ) bool bCanBeRenamed = false; try { uno::Reference< XResultSet > xResultSet; Sequence< OUString > aProps { TARGET_URL }; xResultSet = aGroup.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY ); if ( xResultSet.is() ) { uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW ); uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW ); while ( xResultSet->next() ) { if ( !::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, xRow->getString( 1 ) ) ) throw uno::Exception(u"not sub path"_ustr, nullptr); } bCanBeRenamed = true; } } catch ( Exception& ) {} if ( bCanBeRenamed ) { INetURLObject aGroupTargetObj( aGroupTargetURL ); const OUString aFsysName = aGroupTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); if ( aGroupTargetObj.removeSegment() && ReplaceUINamesForTemplateDir_Impl( aGroupTargetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aFsysName, rOldName, rNewName ) ) { // rename the group in the hierarchy Any aTitleValue; aTitleValue <<= rNewName; return setProperty( aGroup, TITLE, aTitleValue ); } } return false; } sal_Bool SfxDocTplService::storeTemplate( const OUString& rGroupName, const OUString& rTemplateName, const uno::Reference< frame::XStorable >& rStorable ) { if (!init()) return false; ::osl::MutexGuard aGuard( maMutex ); // Check, whether or not there is a group with this name // Return false, if there is no group with the given name Content aGroup, aTemplateToRemove; INetURLObject aGroupObj( maRootURL ); bool bRemoveOldTemplateContent = false; aGroupObj.insertName( rGroupName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) return false; OUString aGroupTargetURL; Any aValue; if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) ) aValue >>= aGroupTargetURL; // Check, if there's a template with the given name in this group // the target template should be overwritten if it is imported by user // in case the template is installed by office installation of by an add-in // it can not be replaced aGroupObj.insertName( rTemplateName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; OUString aTemplateToRemoveTargetURL; if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplateToRemove ) ) { bRemoveOldTemplateContent = true; if ( getProperty( aTemplateToRemove, TARGET_URL, aValue ) ) aValue >>= aTemplateToRemoveTargetURL; if ( aGroupTargetURL.isEmpty() || !maTemplateDirs.hasElements() || (!aTemplateToRemoveTargetURL.isEmpty() && isInternalTemplateDir(aTemplateToRemoveTargetURL)) ) return false; // it is not allowed to remove the template } try { const uno::Reference< uno::XComponentContext >& xContext = ::comphelper::getProcessComponentContext(); // get document service name uno::Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xContext) ); const OUString sDocServiceName {xModuleManager->identify( uno::Reference< uno::XInterface >( rStorable, uno::UNO_QUERY ) )}; if ( sDocServiceName.isEmpty() ) throw uno::RuntimeException(); // get the actual filter name uno::Reference< lang::XMultiServiceFactory > xConfigProvider = configuration::theDefaultProvider::get( xContext ); uno::Sequence aArgs(comphelper::InitAnyPropertySequence( { {"nodepath", uno::Any(u"/org.openoffice.Setup/Office/Factories/"_ustr)} })); uno::Reference< container::XNameAccess > xSOFConfig( xConfigProvider->createInstanceWithArguments( u"com.sun.star.configuration.ConfigurationAccess"_ustr, aArgs ), uno::UNO_QUERY_THROW ); uno::Reference< container::XNameAccess > xApplConfig; xSOFConfig->getByName( sDocServiceName ) >>= xApplConfig; if ( !xApplConfig.is() ) throw uno::RuntimeException(); OUString aFilterName; xApplConfig->getByName(u"ooSetupFactoryActualTemplateFilter"_ustr) >>= aFilterName; if ( aFilterName.isEmpty() ) throw uno::RuntimeException(); // find the related type name uno::Reference< container::XNameAccess > xFilterFactory( mxContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.FilterFactory"_ustr, mxContext), uno::UNO_QUERY_THROW ); uno::Sequence< beans::PropertyValue > aFilterData; xFilterFactory->getByName( aFilterName ) >>= aFilterData; OUString aTypeName; for (const auto& rProp : aFilterData) if ( rProp.Name == "Type" ) rProp.Value >>= aTypeName; if ( aTypeName.isEmpty() ) throw uno::RuntimeException(); // find the mediatype and extension uno::Reference< container::XNameAccess > xTypeDetection = mxType.is() ? uno::Reference< container::XNameAccess >( mxType, uno::UNO_QUERY_THROW ) : uno::Reference< container::XNameAccess >( mxContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.TypeDetection"_ustr, mxContext), uno::UNO_QUERY_THROW ); SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aTypeName ) ); uno::Sequence< OUString > aAllExt = aTypeProps.getUnpackedValueOrDefault(u"Extensions"_ustr, Sequence< OUString >() ); if ( !aAllExt.hasElements() ) throw uno::RuntimeException(); const OUString aMediaType {aTypeProps.getUnpackedValueOrDefault(u"MediaType"_ustr, OUString() )}; const OUString& aExt {aAllExt[0]}; if ( aMediaType.isEmpty() || aExt.isEmpty() ) throw uno::RuntimeException(); // construct destination url if ( aGroupTargetURL.isEmpty() ) { aGroupTargetURL = CreateNewGroupFsys( rGroupName, aGroup ); if ( aGroupTargetURL.isEmpty() ) throw uno::RuntimeException(); } OUString aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, rTemplateName, aExt ); if ( aNewTemplateTargetURL.isEmpty() ) { aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, u"UserTemplate"_ustr, aExt ); if ( aNewTemplateTargetURL.isEmpty() ) throw uno::RuntimeException(); } // store template uno::Sequence< PropertyValue > aStoreArgs{ comphelper::makePropertyValue(u"FilterName"_ustr, aFilterName), comphelper::makePropertyValue(u"DocumentTitle"_ustr, rTemplateName) }; if( !::utl::UCBContentHelper::EqualURLs( aNewTemplateTargetURL, rStorable->getLocation() )) rStorable->storeToURL( aNewTemplateTargetURL, aStoreArgs ); else rStorable->store(); // the storing was successful, now the old template with the same name can be removed if it existed if ( !aTemplateToRemoveTargetURL.isEmpty() ) { removeContent( aTemplateToRemoveTargetURL ); /* * pb: #i79496# * if the old template was the standard template * it is necessary to change the standard template with the new file name */ const OUString sStdTmplFile = SfxObjectFactory::GetStandardTemplate( sDocServiceName ); if ( INetURLObject( sStdTmplFile ) == INetURLObject( aTemplateToRemoveTargetURL ) ) { SfxObjectFactory::SetStandardTemplate( sDocServiceName, aNewTemplateTargetURL ); } } if ( bRemoveOldTemplateContent ) removeContent( aTemplateToRemove ); // add the template to hierarchy return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aMediaType ); } catch( Exception& ) { // the template was not stored return false; } } sal_Bool SfxDocTplService::addTemplate( const OUString& rGroupName, const OUString& rTemplateName, const OUString& rSourceURL ) { if (!init()) return false; ::osl::MutexGuard aGuard( maMutex ); // Check, whether or not there is a group with this name // Return false, if there is no group with the given name Content aGroup, aTemplate, aTargetGroup; INetURLObject aGroupObj( maRootURL ); aGroupObj.insertName( rGroupName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) return false; // Check, if there's a template with the given name in this group // Return false, if there already is a template aGroupObj.insertName( rTemplateName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) ) return false; // get the target url of the group OUString aTargetURL; Any aValue; if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) ) aValue >>= aTargetURL; if ( aTargetURL.isEmpty() ) { aTargetURL = CreateNewGroupFsys( rGroupName, aGroup ); if ( aTargetURL.isEmpty() ) return false; } // Get the content type OUString aTitle, aType; bool bDocHasTitle = false; getTitleFromURL( rSourceURL, aTitle, aType, bDocHasTitle ); INetURLObject aSourceObj( rSourceURL ); if ( rTemplateName == aTitle ) { // addTemplate will sometimes be called just to add an entry in the // hierarchy; the target URL and the source URL will be the same in // this scenario // TODO/LATER: get rid of this old hack INetURLObject aTargetObj( aTargetURL ); aTargetObj.insertName( rTemplateName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); aTargetObj.setExtension( aSourceObj.getExtension() ); const OUString aTargetURL2 = aTargetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if ( aTargetURL2 == rSourceURL ) return addEntry( aGroup, rTemplateName, aTargetURL2, aType ); } // copy the template into the new group (targeturl) INetURLObject aTmpURL( aSourceObj ); aTmpURL.CutExtension(); const OUString aPattern {aTmpURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )}; const OUString aNewTemplateTargetURL {CreateNewUniqueFileWithPrefix( aTargetURL, aPattern, aSourceObj.getExtension() )}; INetURLObject aNewTemplateTargetObj( aNewTemplateTargetURL ); const OUString aNewTemplateTargetName {aNewTemplateTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )}; if ( aNewTemplateTargetURL.isEmpty() || aNewTemplateTargetName.isEmpty() ) return false; // get access to source file Content aSourceContent; uno::Reference < ucb::XCommandEnvironment > xEnv; INetURLObject aSourceURL( rSourceURL ); if( ! Content::create( aSourceURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) ) return false; if( ! Content::create( aTargetURL, xEnv, comphelper::getProcessComponentContext(), aTargetGroup ) ) return false; // transfer source file try { aTargetGroup.transferContent( aSourceContent, InsertOperation::Copy, aNewTemplateTargetName, NameClash::OVERWRITE, aType ); // allow to edit the added template Content aResultContent; if ( Content::create( aNewTemplateTargetURL, xEnv, comphelper::getProcessComponentContext(), aResultContent ) ) { static constexpr OUString aPropertyName( u"IsReadOnly"_ustr ); uno::Any aProperty; bool bReadOnly = false; if ( getProperty( aResultContent, aPropertyName, aProperty ) && ( aProperty >>= bReadOnly ) && bReadOnly ) setProperty( aResultContent, aPropertyName, uno::Any( false ) ); } } catch ( ContentCreationException& ) { return false; } catch ( Exception& ) { return false; } // either the document has title and it is the same as requested, or we have to set it bool bCorrectTitle = ( bDocHasTitle && aTitle == rTemplateName ); if ( !bCorrectTitle ) { if ( !bDocHasTitle ) { INetURLObject aNewTmpObj(std::move(aNewTemplateTargetObj)); aNewTmpObj.CutExtension(); bCorrectTitle = ( aNewTmpObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ) == rTemplateName ); } if ( !bCorrectTitle ) bCorrectTitle = setTitleForURL( aNewTemplateTargetURL, rTemplateName ); } if ( bCorrectTitle ) { // create a new entry in the hierarchy return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aType ); } // TODO/LATER: The user could be notified here that the renaming has failed // create a new entry in the hierarchy addEntry( aGroup, aTitle, aNewTemplateTargetURL, aType ); return false; } bool SfxDocTplService::isInternalTemplateDir(const OUString& rURL) const { return std::any_of(maInternalTemplateDirs.begin(), maInternalTemplateDirs.end(), [&rURL](const OUString& rDir) { return ::utl::UCBContentHelper::IsSubPath(rDir, rURL); }); } OUString SfxDocTplService::findParentTemplateDir(const OUString& rURL) const { const OUString* pDirs = std::find_if(maTemplateDirs.begin(), maTemplateDirs.end(), [&rURL](const OUString& rDir) { return ::utl::UCBContentHelper::IsSubPath(rDir, rURL); }); if (pDirs != maTemplateDirs.end()) return *pDirs; return OUString(); } sal_Bool SfxDocTplService::removeTemplate( const OUString& rGroupName, const OUString& rTemplateName ) { if (!init()) return false; ::osl::MutexGuard aGuard( maMutex ); // Check, whether or not there is a group with this name // Return false, if there is no group with the given name Content aGroup, aTemplate; INetURLObject aGroupObj( maRootURL ); aGroupObj.insertName( rGroupName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) return false; // Check, if there's a template with the given name in this group // Return false, if there is no template aGroupObj.insertName( rTemplateName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( !Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) ) return false; // get the target URL from the template OUString aTargetURL; Any aValue; if ( getProperty( aTemplate, TARGET_URL, aValue ) ) aValue >>= aTargetURL; // delete the target template if ( !aTargetURL.isEmpty() ) { if (isInternalTemplateDir(aTargetURL)) return false; removeContent( aTargetURL ); } // delete the template entry return removeContent( aTemplate ); } sal_Bool SfxDocTplService::renameTemplate( const OUString& rGroupName, const OUString& rOldName, const OUString& rNewName ) { if ( rOldName == rNewName ) return true; if (!init()) return false; ::osl::MutexGuard aGuard( maMutex ); // Check, whether or not there is a group with this name // Return false, if there is no group with the given name Content aGroup, aTemplate; INetURLObject aGroupObj( maRootURL ); aGroupObj.insertName( rGroupName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) return false; // Check, if there's a template with the new name in this group // Return false, if there is one aGroupObj.insertName( rNewName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) ) return false; // Check, if there's a template with the old name in this group // Return false, if there is no template aGroupObj.removeSegment(); aGroupObj.insertName( rOldName, false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); aTemplateURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); if ( !Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) ) return false; OUString aTemplateTargetURL; Any aTargetValue; if ( getProperty( aTemplate, TARGET_URL, aTargetValue ) ) aTargetValue >>= aTemplateTargetURL; if ( !setTitleForURL( aTemplateTargetURL, rNewName ) ) return false; // rename the template entry in the cache Any aTitleValue; aTitleValue <<= rNewName; return setProperty( aTemplate, TITLE, aTitleValue ); } //--- XDocumentTemplates --- uno::Reference< ucb::XContent > SAL_CALL SfxDocTplService::getContent() { if ( init() ) return maRootContent.get(); return nullptr; } WaitWindow_Impl::WaitWindow_Impl() : WorkWindow(nullptr, WB_BORDER | WB_3DLOOK) { tools::Rectangle aRect(0, 0, 300, 30000); maText = SfxResId(RID_CNT_STR_WAITING); maRect = GetTextRect(aRect, maText, gnTextStyle); aRect = maRect; aRect.AdjustRight(2 * X_OFFSET ); aRect.AdjustBottom(2 * Y_OFFSET ); maRect.SetPos(Point(X_OFFSET, Y_OFFSET)); SetOutputSizePixel(aRect.GetSize()); Show(); PaintImmediately(); GetOutDev()->Flush(); } WaitWindow_Impl::~WaitWindow_Impl() { disposeOnce(); } void WaitWindow_Impl::dispose() { Hide(); WorkWindow::dispose(); } void WaitWindow_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) { rRenderContext.DrawText(maRect, maText, gnTextStyle); } void SfxDocTplService::addHierGroup( GroupList_Impl& rList, const OUString& rTitle, const OUString& rOwnURL ) { // now get the content of the Group Content aContent; uno::Reference xResultSet; try { aContent = Content(rOwnURL, maCmdEnv, comphelper::getProcessComponentContext()); xResultSet = aContent.createCursor( { TITLE, TARGET_URL, PROPERTY_TYPE }, INCLUDE_DOCUMENTS_ONLY ); } catch (ContentCreationException&) { TOOLS_WARN_EXCEPTION( "sfx.doc", "" ); } catch (Exception&) {} if ( !xResultSet.is() ) return; GroupData_Impl *pGroup = new GroupData_Impl( rTitle ); pGroup->setHierarchy( true ); pGroup->setHierarchyURL( rOwnURL ); rList.push_back( std::unique_ptr(pGroup) ); uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); uno::Reference< XRow > xRow( xResultSet, UNO_QUERY ); try { while ( xResultSet->next() ) { bool bUpdateType = false; DocTemplates_EntryData_Impl *pData; const OUString aTitle( xRow->getString( 1 ) ); const OUString aTargetDir( xRow->getString( 2 ) ); OUString aType( xRow->getString( 3 ) ); const OUString aHierURL {xContentAccess->queryContentIdentifierString()}; if ( aType.isEmpty() ) { OUString aTmpTitle; bool bDocHasTitle = false; getTitleFromURL( aTargetDir, aTmpTitle, aType, bDocHasTitle ); if ( !aType.isEmpty() ) bUpdateType = true; } pData = pGroup->addEntry( aTitle, aTargetDir, aType, aHierURL ); pData->setUpdateType( bUpdateType ); } } catch ( Exception& ) {} } void SfxDocTplService::addFsysGroup( GroupList_Impl& rList, const OUString& rTitle, const OUString& rUITitle, const OUString& rOwnURL, bool bWriteableGroup ) { OUString aTitle; if ( rUITitle.isEmpty() ) { // reserved FS names that should not be used if ( rTitle == "wizard" ) return; else if ( rTitle == "internal" ) return; aTitle = getLongName( rTitle ); } else aTitle = rUITitle; if ( aTitle.isEmpty() ) return; GroupData_Impl* pGroup = nullptr; for (const std::unique_ptr& i : rList) { if ( i->getTitle() == aTitle ) { pGroup = i.get(); break; } } if ( !pGroup ) { pGroup = new GroupData_Impl( aTitle ); rList.push_back( std::unique_ptr(pGroup) ); } if ( bWriteableGroup ) pGroup->setTargetURL( rOwnURL ); pGroup->setInUse(); // now get the content of the Group Content aContent; uno::Reference< XResultSet > xResultSet; Sequence< OUString > aProps { TITLE }; try { // this method is only used during checking of the available template-folders // that should happen quietly uno::Reference< XCommandEnvironment > aQuietEnv; aContent = Content( rOwnURL, aQuietEnv, comphelper::getProcessComponentContext() ); xResultSet = aContent.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY ); } catch ( Exception& ) {} if ( !xResultSet.is() ) return; uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); uno::Reference< XRow > xRow( xResultSet, UNO_QUERY ); try { while ( xResultSet->next() ) { OUString aChildTitle( xRow->getString( 1 ) ); const OUString aTargetURL {xContentAccess->queryContentIdentifierString()}; OUString aType; if ( aChildTitle == "sfx.tlx" || aChildTitle == "groupuinames.xml" ) continue; bool bDocHasTitle = false; getTitleFromURL( aTargetURL, aChildTitle, aType, bDocHasTitle ); pGroup->addEntry( aChildTitle, aTargetURL, aType, OUString() ); } } catch ( Exception& ) {} } void SfxDocTplService::createFromContent( GroupList_Impl& rList, Content &rContent, bool bHierarchy, bool bWriteableContent ) { const OUString aTargetURL {rContent.get()->getIdentifier()->getContentIdentifier()}; // when scanning the file system, we have to add the 'standard' group, too if ( ! bHierarchy ) { const OUString aUIStdTitle {getLongName( STANDARD_FOLDER )}; addFsysGroup( rList, OUString(), aUIStdTitle, aTargetURL, bWriteableContent ); } // search for predefined UI names INetURLObject aLayerObj( aTargetURL ); // TODO/LATER: Use hashmap in future std::vector< beans::StringPair > aUINames; if ( !bHierarchy ) aUINames = ReadUINamesForTemplateDir_Impl( aLayerObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); uno::Reference< XResultSet > xResultSet; Sequence< OUString > aProps { TITLE }; try { xResultSet = rContent.createCursor( aProps, INCLUDE_FOLDERS_ONLY ); } catch ( Exception& ) {} if ( !xResultSet.is() ) return; uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); uno::Reference< XRow > xRow( xResultSet, UNO_QUERY ); try { while ( xResultSet->next() ) { // TODO/LATER: clarify the encoding of the Title const OUString aTitle( xRow->getString( 1 ) ); const OUString aTargetSubfolderURL( xContentAccess->queryContentIdentifierString() ); if ( bHierarchy ) addHierGroup( rList, aTitle, aTargetSubfolderURL ); else { OUString aUITitle; for (const beans::StringPair & rUIName : aUINames) if ( rUIName.First == aTitle ) { aUITitle = rUIName.Second; break; } addFsysGroup( rList, aTitle, aUITitle, aTargetSubfolderURL, bWriteableContent ); } } } catch ( Exception& ) {} } void SfxDocTplService::removeFromHierarchy( DocTemplates_EntryData_Impl const *pData ) { Content aTemplate; if ( Content::create( pData->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) ) { removeContent( aTemplate ); } } void SfxDocTplService::addToHierarchy( GroupData_Impl const *pGroup, DocTemplates_EntryData_Impl const *pData ) { Content aGroup, aTemplate; if ( ! Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) return; // Check, if there's a template with the given name in this group // Return if there is already a template INetURLObject aGroupObj( pGroup->getHierarchyURL() ); aGroupObj.insertName( pData->getTitle(), false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) ) return; addEntry( aGroup, pData->getTitle(), pData->getTargetURL(), pData->getType() ); } void SfxDocTplService::updateData( DocTemplates_EntryData_Impl const *pData ) { Content aTemplate; if ( ! Content::create( pData->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) ) return; if ( pData->getUpdateType() ) { setProperty( aTemplate, PROPERTY_TYPE, Any( pData->getType() ) ); } if ( pData->getUpdateLink() ) { setProperty( aTemplate, TARGET_URL, Any( pData->getTargetURL() ) ); } } void SfxDocTplService::addGroupToHierarchy( GroupData_Impl *pGroup ) { Content aGroup; INetURLObject aNewGroupObj( maRootURL ); aNewGroupObj.insertName( pGroup->getTitle(), false, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); const OUString aNewGroupURL {aNewGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )}; if ( createFolder( aNewGroupURL, false, false, aGroup ) ) { setProperty( aGroup, TARGET_DIR_URL, Any( pGroup->getTargetURL() ) ); pGroup->setHierarchyURL( aNewGroupURL ); size_t nCount = pGroup->count(); for ( size_t i = 0; i < nCount; i++ ) { DocTemplates_EntryData_Impl *pData = pGroup->getEntry( i ); addToHierarchy( pGroup, pData ); // add entry to hierarchy } } } void SfxDocTplService::removeFromHierarchy( GroupData_Impl const *pGroup ) { Content aGroup; if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) ) { removeContent( aGroup ); } } GroupData_Impl::GroupData_Impl( OUString aTitle ) : maTitle(std::move(aTitle)), mbInUse(false), mbInHierarchy(false) { } DocTemplates_EntryData_Impl* GroupData_Impl::addEntry( const OUString& rTitle, const OUString& rTargetURL, const OUString& rType, const OUString& rHierURL ) { DocTemplates_EntryData_Impl* pData = nullptr; bool EntryFound = false; for (auto const & p : maEntries) { pData = p.get(); if ( pData->getTitle() == rTitle ) { EntryFound = true; break; } } if ( !EntryFound ) { pData = new DocTemplates_EntryData_Impl( rTitle ); pData->setTargetURL( rTargetURL ); pData->setType( rType ); if ( !rHierURL.isEmpty() ) { pData->setHierarchyURL( rHierURL ); pData->setHierarchy( true ); } maEntries.emplace_back( pData ); } else { if ( !rHierURL.isEmpty() ) { pData->setHierarchyURL( rHierURL ); pData->setHierarchy( true ); } if ( pData->getInHierarchy() ) pData->setInUse(); if ( rTargetURL != pData->getTargetURL() ) { pData->setTargetURL( rTargetURL ); pData->setUpdateLink( true ); } } return pData; } DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( OUString aTitle ) : maTitle(std::move(aTitle)), mbInHierarchy(false), mbInUse(false), mbUpdateType(false), mbUpdateLink(false) { } } // static bool SfxURLRelocator_Impl::propertyCanContainOfficeDir( std::u16string_view rPropName ) { // Note: TargetURL is handled by UCB itself (because it is a property // with a predefined semantic). Additional Core properties introduced // be a client app must be handled by the client app itself, because // the UCB does not know the semantics of those properties. return ( rPropName == TARGET_DIR_URL || rPropName == PROPERTY_DIRLIST ); } SfxURLRelocator_Impl::SfxURLRelocator_Impl( uno::Reference< XComponentContext > xContext ) : mxContext(std::move( xContext )) { } SfxURLRelocator_Impl::~SfxURLRelocator_Impl() { } void SfxURLRelocator_Impl::initOfficeInstDirs() { if ( !mxOfficeInstDirs.is() ) { std::scoped_lock aGuard( maMutex ); if ( !mxOfficeInstDirs.is() ) { OSL_ENSURE( mxContext.is(), "No service manager!" ); mxOfficeInstDirs = theOfficeInstallationDirectories::get(mxContext); } } } void SfxURLRelocator_Impl::implExpandURL( OUString& io_url ) { const INetURLObject aParser( io_url ); if ( aParser.GetProtocol() != INetProtocol::VndSunStarExpand ) return; io_url = aParser.GetURLPath( INetURLObject::DecodeMechanism::WithCharset ); try { if ( !mxMacroExpander.is() ) { mxMacroExpander.set( theMacroExpander::get(mxContext), UNO_SET_THROW ); } io_url = mxMacroExpander->expandMacros( io_url ); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("sfx.doc"); } } void SfxURLRelocator_Impl::makeRelocatableURL( OUString & rURL ) { if ( !rURL.isEmpty() ) { initOfficeInstDirs(); implExpandURL( rURL ); rURL = mxOfficeInstDirs->makeRelocatableURL( rURL ); } } void SfxURLRelocator_Impl::makeAbsoluteURL( OUString & rURL ) { if ( !rURL.isEmpty() ) { initOfficeInstDirs(); implExpandURL( rURL ); rURL = mxOfficeInstDirs->makeAbsoluteURL( rURL ); } } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_sfx2_DocumentTemplates_get_implementation( css::uno::XComponentContext *context, css::uno::Sequence const &) { return cppu::acquire(new SfxDocTplService(context)); } OUString DocTemplLocaleHelper::GetStandardGroupString() { return SfxResId(TEMPLATE_LONG_NAMES_ARY[0]); } std::vector DocTemplLocaleHelper::GetBuiltInGroupNames() { std::vector aGroups; for(auto const & aGroupName : TEMPLATE_LONG_NAMES_ARY) aGroups.push_back(SfxResId(aGroupName)); return aGroups; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */