1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/mutex.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <tools/urlobj.hxx>
23 #include <rtl/ustring.hxx>
24 #include <sal/log.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/wrkwin.hxx>
27 #include <unotools/pathoptions.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <comphelper/propertysequence.hxx>
30 #include <comphelper/propertyvalue.hxx>
31 #include <comphelper/sequenceashashmap.hxx>
32 #include <comphelper/storagehelper.hxx>
33 #include <comphelper/string.hxx>
34 #include <cppuhelper/implbase.hxx>
35 #include <cppuhelper/supportsservice.hxx>
36 #include <com/sun/star/beans/IllegalTypeException.hpp>
37 #include <com/sun/star/beans/PropertyAttribute.hpp>
38 #include <com/sun/star/beans/PropertyExistException.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/beans/XPropertySetInfo.hpp>
41 #include <com/sun/star/beans/XPropertyContainer.hpp>
42 #include <com/sun/star/beans/StringPair.hpp>
43 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
44 #include <com/sun/star/util/theMacroExpander.hpp>
45 #include <com/sun/star/util/theOfficeInstallationDirectories.hpp>
46 #include <com/sun/star/configuration/theDefaultProvider.hpp>
47 #include <com/sun/star/document/XTypeDetection.hpp>
48 #include <com/sun/star/document/DocumentProperties.hpp>
49 #include <com/sun/star/io/TempFile.hpp>
50 #include <com/sun/star/sdbc/XResultSet.hpp>
51 #include <com/sun/star/sdbc/XRow.hpp>
52 #include <com/sun/star/ucb/ContentCreationException.hpp>
53 #include <com/sun/star/ucb/NameClash.hpp>
54 #include <com/sun/star/ucb/NameClashException.hpp>
55 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
56 #include <com/sun/star/ucb/XContentAccess.hpp>
57 #include <com/sun/star/frame/ModuleManager.hpp>
58 #include <com/sun/star/uno/Exception.hpp>
59 #include <com/sun/star/task/InteractionHandler.hpp>
60 #include <com/sun/star/ucb/XProgressHandler.hpp>
61 #include <com/sun/star/container/XNameAccess.hpp>
62 #include <com/sun/star/frame/XDocumentTemplates.hpp>
63 #include <com/sun/star/frame/XStorable.hpp>
64 #include <com/sun/star/lang/Locale.hpp>
65 #include <com/sun/star/lang/XLocalizable.hpp>
66 #include <com/sun/star/lang/XServiceInfo.hpp>
67 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
68 #include <com/sun/star/ucb/XContent.hpp>
69 #include <com/sun/star/beans/PropertyValue.hpp>
70 #include <com/sun/star/uno/RuntimeException.hpp>
71 #include <com/sun/star/uno/XComponentContext.hpp>
72 #include <com/sun/star/util/thePathSettings.hpp>
74 #include <svtools/templatefoldercache.hxx>
75 #include <unotools/configmgr.hxx>
76 #include <unotools/ucbhelper.hxx>
77 #include <i18nlangtag/languagetag.hxx>
78 #include <ucbhelper/content.hxx>
80 #include <sfx2/sfxresid.hxx>
81 #include <sfxurlrelocator.hxx>
82 #include "doctemplateslocal.hxx"
83 #include <sfx2/docfac.hxx>
84 #include <sfx2/strings.hrc>
85 #include <doctempl.hrc>
90 constexpr OUStringLiteral SERVICENAME_TYPEDETECTION
= u
"com.sun.star.document.TypeDetection";
92 constexpr OUStringLiteral TEMPLATE_ROOT_URL
= u
"vnd.sun.star.hier:/templates";
93 constexpr OUStringLiteral TITLE
= u
"Title";
94 constexpr OUStringLiteral IS_FOLDER
= u
"IsFolder";
95 constexpr OUStringLiteral IS_DOCUMENT
= u
"IsDocument";
96 constexpr OUStringLiteral TARGET_URL
= u
"TargetURL";
97 constexpr OUStringLiteral TEMPLATE_VERSION
= u
"TemplateComponentVersion";
98 constexpr OUStringLiteral TEMPLATE_VERSION_VALUE
= u
"2";
99 constexpr OUStringLiteral TYPE_FOLDER
= u
"application/vnd.sun.star.hier-folder";
100 constexpr OUStringLiteral TYPE_LINK
= u
"application/vnd.sun.star.hier-link";
101 constexpr OUStringLiteral TYPE_FSYS_FOLDER
= u
"application/vnd.sun.staroffice.fsys-folder";
102 constexpr OUStringLiteral TYPE_FSYS_FILE
= u
"application/vnd.sun.staroffice.fsys-file";
104 constexpr OUStringLiteral PROPERTY_DIRLIST
= u
"DirectoryList";
105 constexpr OUStringLiteral PROPERTY_NEEDSUPDATE
= u
"NeedsUpdate";
106 constexpr OUStringLiteral PROPERTY_TYPE
= u
"TypeDescription";
108 constexpr OUStringLiteral TARGET_DIR_URL
= u
"TargetDirURL";
109 constexpr OUStringLiteral COMMAND_DELETE
= u
"delete";
111 constexpr OUStringLiteral STANDARD_FOLDER
= u
"standard";
115 using namespace ::com::sun::star
;
116 using namespace ::com::sun::star::beans
;
117 using namespace ::com::sun::star::document
;
118 using namespace ::com::sun::star::io
;
119 using namespace ::com::sun::star::lang
;
120 using namespace ::com::sun::star::sdbc
;
121 using namespace ::com::sun::star::ucb
;
122 using namespace ::com::sun::star::uno
;
123 using namespace ::com::sun::star::container
;
124 using namespace ::com::sun::star::util
;
126 using namespace ::ucbhelper
;
127 using namespace ::comphelper
;
133 class WaitWindow_Impl
: public WorkWindow
135 tools::Rectangle maRect
;
137 static constexpr DrawTextFlags gnTextStyle
= DrawTextFlags::Center
| DrawTextFlags::VCenter
| DrawTextFlags::WordBreak
| DrawTextFlags::MultiLine
;
141 virtual ~WaitWindow_Impl() override
;
142 virtual void dispose() override
;
143 virtual void Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
) override
;
152 OUString maShortName
;
156 class DocTemplates_EntryData_Impl
;
157 class GroupData_Impl
;
159 typedef vector
< std::unique_ptr
<GroupData_Impl
> > GroupList_Impl
;
162 class TplTaskEnvironment
: public ::cppu::WeakImplHelper
< ucb::XCommandEnvironment
>
164 uno::Reference
< task::XInteractionHandler
> m_xInteractionHandler
;
167 explicit TplTaskEnvironment( const uno::Reference
< task::XInteractionHandler
>& rxInteractionHandler
)
168 : m_xInteractionHandler( rxInteractionHandler
)
171 virtual uno::Reference
<task::XInteractionHandler
> SAL_CALL
getInteractionHandler() override
172 { return m_xInteractionHandler
; }
174 virtual uno::Reference
<ucb::XProgressHandler
> SAL_CALL
getProgressHandler() override
175 { return uno::Reference
<ucb::XProgressHandler
>(); }
178 class SfxDocTplService_Impl
180 uno::Reference
< XComponentContext
> mxContext
;
181 uno::Reference
< XCommandEnvironment
> maCmdEnv
;
182 uno::Reference
< XDocumentProperties
> m_xDocProps
;
183 uno::Reference
< XTypeDetection
> mxType
;
185 ::osl::Mutex maMutex
;
186 Sequence
< OUString
> maTemplateDirs
;
187 Sequence
< OUString
> maInternalTemplateDirs
;
189 std::vector
< NamePair_Impl
> maNames
;
190 lang::Locale maLocale
;
191 Content maRootContent
;
192 bool mbIsInitialized
: 1;
193 bool mbLocaleSet
: 1;
195 SfxURLRelocator_Impl maRelocator
;
198 void getDefaultLocale();
200 void readFolderList();
202 OUString
getLongName( const OUString
& rShortName
);
203 bool setTitleForURL( const OUString
& rURL
, const OUString
& aTitle
);
204 void getTitleFromURL( const OUString
& rURL
, OUString
& aTitle
, OUString
& aType
, bool& bDocHasTitle
);
206 bool addEntry( Content
& rParentFolder
,
207 const OUString
& rTitle
,
208 const OUString
& rTargetURL
,
209 const OUString
& rType
);
211 bool createFolder( const OUString
& rNewFolderURL
,
214 Content
&rNewFolder
);
216 static bool CreateNewUniqueFolderWithPrefix( const OUString
& aPath
,
217 const OUString
& aPrefix
,
218 OUString
& aNewFolderName
,
219 OUString
& aNewFolderURL
,
220 Content
& aNewFolder
);
221 static OUString
CreateNewUniqueFileWithPrefix( const OUString
& aPath
,
222 const OUString
& aPrefix
,
223 const OUString
& aExt
);
225 std::vector
< beans::StringPair
> ReadUINamesForTemplateDir_Impl( const OUString
& aUserPath
);
226 bool UpdateUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
227 const OUString
& aGroupName
,
228 const OUString
& aNewFolderName
);
229 bool ReplaceUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
230 const OUString
& aFsysGroupName
,
231 std::u16string_view aOldGroupName
,
232 const OUString
& aNewGroupName
);
233 void RemoveUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
234 std::u16string_view aGroupName
);
235 bool WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
236 const std::vector
< beans::StringPair
>& aUINames
);
238 OUString
CreateNewGroupFsys( const OUString
& rGroupName
, Content
& aGroup
);
240 static bool removeContent( Content
& rContent
);
241 bool removeContent( const OUString
& rContentURL
);
243 bool setProperty( Content
& rContent
,
244 const OUString
& rPropName
,
245 const Any
& rPropValue
);
246 bool getProperty( Content
& rContent
,
247 const OUString
& rPropName
,
250 void createFromContent( GroupList_Impl
& rList
,
253 bool bWriteableContent
);
254 void addHierGroup( GroupList_Impl
& rList
,
255 const OUString
& rTitle
,
256 const OUString
& rOwnURL
);
257 void addFsysGroup( GroupList_Impl
& rList
,
258 const OUString
& rTitle
,
259 const OUString
& rUITitle
,
260 const OUString
& rOwnURL
,
261 bool bWriteableGroup
);
262 void removeFromHierarchy( DocTemplates_EntryData_Impl
const *pData
);
263 void addToHierarchy( GroupData_Impl
const *pGroup
,
264 DocTemplates_EntryData_Impl
const *pData
);
266 void removeFromHierarchy( GroupData_Impl
const *pGroup
);
267 void addGroupToHierarchy( GroupData_Impl
*pGroup
);
269 void updateData( DocTemplates_EntryData_Impl
const *pData
);
271 //See: #i66157# and rhbz#1065807
272 //return which template dir the rURL is a subpath of
273 OUString
findParentTemplateDir(const OUString
& rURL
) const;
275 //See: #i66157# and rhbz#1065807
276 //return true if rURL is a path (or subpath of) a dir which is not a user path
277 //which implies neither it or its contents can be removed
278 bool isInternalTemplateDir(const OUString
& rURL
) const;
280 explicit SfxDocTplService_Impl( const uno::Reference
< XComponentContext
> & xContext
);
281 ~SfxDocTplService_Impl();
283 bool init() { if ( !mbIsInitialized
) init_Impl(); return mbIsInitialized
; }
284 const Content
& getContent() const { return maRootContent
; }
286 void setLocale( const lang::Locale
& rLocale
);
287 lang::Locale
getLocale();
289 bool storeTemplate( const OUString
& rGroupName
,
290 const OUString
& rTemplateName
,
291 const uno::Reference
< frame::XStorable
>& rStorable
);
293 bool addTemplate( const OUString
& rGroupName
,
294 const OUString
& rTemplateName
,
295 const OUString
& rSourceURL
);
296 bool removeTemplate( std::u16string_view rGroupName
,
297 std::u16string_view rTemplateName
);
298 bool renameTemplate( std::u16string_view rGroupName
,
299 std::u16string_view rOldName
,
300 const OUString
& rNewName
);
302 bool addGroup( const OUString
& rGroupName
);
303 bool removeGroup( std::u16string_view rGroupName
);
304 bool renameGroup( std::u16string_view rOldName
,
305 const OUString
& rNewName
);
312 class DocTemplates_EntryData_Impl
316 OUString maTargetURL
;
317 OUString maHierarchyURL
;
319 bool mbInHierarchy
: 1;
321 bool mbUpdateType
: 1;
322 bool mbUpdateLink
: 1;
325 explicit DocTemplates_EntryData_Impl( const OUString
& rTitle
);
327 void setInUse() { mbInUse
= true; }
328 void setHierarchy( bool bInHierarchy
) { mbInHierarchy
= bInHierarchy
; }
329 void setUpdateLink( bool bUpdateLink
) { mbUpdateLink
= bUpdateLink
; }
330 void setUpdateType( bool bUpdateType
) { mbUpdateType
= bUpdateType
; }
332 bool getInUse() const { return mbInUse
; }
333 bool getInHierarchy() const { return mbInHierarchy
; }
334 bool getUpdateLink() const { return mbUpdateLink
; }
335 bool getUpdateType() const { return mbUpdateType
; }
337 const OUString
& getHierarchyURL() const { return maHierarchyURL
; }
338 const OUString
& getTargetURL() const { return maTargetURL
; }
339 const OUString
& getTitle() const { return maTitle
; }
340 const OUString
& getType() const { return maType
; }
342 void setHierarchyURL( const OUString
& rURL
) { maHierarchyURL
= rURL
; }
343 void setTargetURL( const OUString
& rURL
) { maTargetURL
= rURL
; }
344 void setType( const OUString
& rType
) { maType
= rType
; }
350 std::vector
< std::unique_ptr
<DocTemplates_EntryData_Impl
> > maEntries
;
352 OUString maHierarchyURL
;
353 OUString maTargetURL
;
355 bool mbInHierarchy
: 1;
358 explicit GroupData_Impl( const OUString
& rTitle
);
360 void setInUse() { mbInUse
= true; }
361 void setHierarchy( bool bInHierarchy
) { mbInHierarchy
= bInHierarchy
; }
362 void setHierarchyURL( const OUString
& rURL
) { maHierarchyURL
= rURL
; }
363 void setTargetURL( const OUString
& rURL
) { maTargetURL
= rURL
; }
365 bool getInUse() const { return mbInUse
; }
366 bool getInHierarchy() const { return mbInHierarchy
; }
367 const OUString
& getHierarchyURL() const { return maHierarchyURL
; }
368 const OUString
& getTargetURL() const { return maTargetURL
; }
369 const OUString
& getTitle() const { return maTitle
; }
371 DocTemplates_EntryData_Impl
* addEntry( const OUString
& rTitle
,
372 const OUString
& rTargetURL
,
373 const OUString
& rType
,
374 const OUString
& rHierURL
);
375 size_t count() { return maEntries
.size(); }
376 DocTemplates_EntryData_Impl
* getEntry( size_t nPos
) { return maEntries
[ nPos
].get(); }
380 // private SfxDocTplService_Impl
382 void SfxDocTplService_Impl::init_Impl()
384 uno::Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
385 uno::Reference
< task::XInteractionHandler
> xInteractionHandler(
386 task::InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
387 maCmdEnv
= new TplTaskEnvironment( xInteractionHandler
);
389 ::osl::ClearableMutexGuard
aGuard( maMutex
);
390 bool bIsInitialized
= false;
391 bool bNeedsUpdate
= false;
396 // convert locale to string
397 // set maRootContent to the root of the templates hierarchy. Create the
398 // entry if necessary
400 maRootURL
= TEMPLATE_ROOT_URL
+ "/" + LanguageTag::convertToBcp47(maLocale
);
402 const OUString
aTemplVersPropName( TEMPLATE_VERSION
);
403 const OUString
aTemplVers( TEMPLATE_VERSION_VALUE
);
404 if ( Content::create( maRootURL
, maCmdEnv
, comphelper::getProcessComponentContext(), maRootContent
) )
408 if ( getProperty( maRootContent
, aTemplVersPropName
, aValue
)
409 && ( aValue
>>= aPropValue
)
410 && aPropValue
== aTemplVers
)
412 bIsInitialized
= true;
415 removeContent( maRootContent
);
418 if ( !bIsInitialized
)
420 if ( createFolder( maRootURL
, true, false, maRootContent
)
421 && setProperty( maRootContent
, aTemplVersPropName
, uno::makeAny( aTemplVers
) ) )
422 bIsInitialized
= true;
427 if ( bIsInitialized
)
430 m_xDocProps
.set(document::DocumentProperties::create(
431 ::comphelper::getProcessComponentContext()));
432 } catch (uno::RuntimeException
const&) {
433 TOOLS_WARN_EXCEPTION("sfx.doc", "SfxDocTplService_Impl::init_Impl: cannot create DocumentProperties service:");
436 mxType
.set( mxContext
->getServiceManager()->createInstanceWithContext(SERVICENAME_TYPEDETECTION
, mxContext
), UNO_QUERY
);
444 SolarMutexClearableGuard aSolarGuard
;
446 VclPtrInstance
< WaitWindow_Impl
> pWin
;
449 osl::MutexGuard
anotherGuard(maMutex
);
452 SolarMutexGuard aSecondSolarGuard
;
454 pWin
.disposeAndClear();
456 else if ( needsUpdate() )
457 // the UI should be shown only on the first update
462 SAL_WARN( "sfx.doc", "init_Impl(): Could not create root" );
465 mbIsInitialized
= bIsInitialized
;
469 void SfxDocTplService_Impl::getDefaultLocale()
473 ::osl::MutexGuard
aGuard( maMutex
);
476 maLocale
= LanguageTag::convertToLocale( utl::ConfigManager::getUILocale(), false);
482 const char* TEMPLATE_SHORT_NAMES_ARY
[] =
498 void SfxDocTplService_Impl::readFolderList()
500 SolarMutexGuard aGuard
;
502 static_assert( SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY
) == SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY
), "mismatch array lengths" );
503 const size_t nCount
= std::min(SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY
), SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY
));
504 for (size_t i
= 0; i
< nCount
; ++i
)
507 aPair
.maShortName
= OUString::createFromAscii(TEMPLATE_SHORT_NAMES_ARY
[i
]);
508 aPair
.maLongName
= SfxResId(TEMPLATE_LONG_NAMES_ARY
[i
]);
510 maNames
.push_back( aPair
);
515 OUString
SfxDocTplService_Impl::getLongName( const OUString
& rShortName
)
519 for (auto const & rPair
: maNames
)
521 if ( rPair
.maShortName
== rShortName
)
523 aRet
= rPair
.maLongName
;
528 if ( aRet
.isEmpty() )
535 void SfxDocTplService_Impl::getDirList()
539 // Get the template dir list
540 // TODO/LATER: let use service, register listener
542 OUString aDirs
= SvtPathOptions().GetTemplatePath();
543 sal_Int32 nCount
= comphelper::string::getTokenCount(aDirs
, C_DELIM
);
545 maTemplateDirs
= Sequence
< OUString
>( nCount
);
547 uno::Reference
< util::XMacroExpander
> xExpander
= util::theMacroExpander::get(mxContext
);
548 static const OUStringLiteral
aPrefix(
549 u
"vnd.sun.star.expand:" );
552 for (auto& rTemplateDir
: asNonConstRange(maTemplateDirs
))
554 aURL
.SetSmartProtocol( INetProtocol::File
);
555 aURL
.SetURL( aDirs
.getToken( 0, C_DELIM
, nIdx
) );
556 rTemplateDir
= aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
558 if ( xExpander
.is() )
560 const sal_Int32 nIndex
{ rTemplateDir
.indexOf( aPrefix
) };
564 rTemplateDir
= rTemplateDir
.replaceAt(nIndex
, aPrefix
.getLength(), u
"");
565 rTemplateDir
= xExpander
->expandMacros( rTemplateDir
);
569 aValue
<<= maTemplateDirs
;
571 css::uno::Reference
< css::util::XPathSettings
> xPathSettings
=
572 css::util::thePathSettings::get(mxContext
);
574 // load internal paths
575 Any aAny
= xPathSettings
->getPropertyValue( "Template_internal" );
576 aAny
>>= maInternalTemplateDirs
;
578 for (auto& rInternalTemplateDir
: asNonConstRange(maInternalTemplateDirs
))
580 //expand vnd.sun.star.expand: and remove "..." from them
581 //to normalize into the expected url patterns
582 maRelocator
.makeRelocatableURL(rInternalTemplateDir
);
583 maRelocator
.makeAbsoluteURL(rInternalTemplateDir
);
586 // Store the template dir list
587 setProperty( maRootContent
, PROPERTY_DIRLIST
, aValue
);
591 bool SfxDocTplService_Impl::needsUpdate()
593 bool bNeedsUpdate
= true;
596 // Get the template dir list
597 bool bHasProperty
= getProperty( maRootContent
, PROPERTY_NEEDSUPDATE
, aValue
);
600 aValue
>>= bNeedsUpdate
;
602 // the old template component also checks this state, but it is initialized from this component
603 // so if this component was already updated the old component does not need such an update
604 ::svt::TemplateFolderCache aTempCache
;
606 bNeedsUpdate
= aTempCache
.needsUpdate();
609 aTempCache
.storeState();
615 bool SfxDocTplService_Impl::setTitleForURL( const OUString
& rURL
, const OUString
& aTitle
)
617 if (m_xDocProps
.is())
621 m_xDocProps
->loadFromMedium(rURL
, Sequence
<PropertyValue
>());
622 m_xDocProps
->setTitle(aTitle
);
624 uno::Reference
< embed::XStorage
> xStorage
= ::comphelper::OStorageHelper::GetStorageFromURL(
625 rURL
, embed::ElementModes::READWRITE
);
627 uno::Sequence
<beans::PropertyValue
> medium( comphelper::InitPropertySequence({
628 { "DocumentBaseURL", Any(rURL
) },
632 m_xDocProps
->storeToStorage(xStorage
, medium
);
643 void SfxDocTplService_Impl::getTitleFromURL( const OUString
& rURL
, OUString
& aTitle
, OUString
& aType
, bool& bDocHasTitle
)
645 bDocHasTitle
= false;
647 if (m_xDocProps
.is())
651 m_xDocProps
->loadFromMedium(rURL
, Sequence
<PropertyValue
>());
652 aTitle
= m_xDocProps
->getTitle();
659 if ( aType
.isEmpty() && mxType
.is() )
661 const OUString aDocType
{mxType
->queryTypeByURL( rURL
)};
662 if ( !aDocType
.isEmpty() )
665 uno::Reference
< container::XNameAccess
> xTypeDetection( mxType
, uno::UNO_QUERY_THROW
);
666 SequenceAsHashMap
aTypeProps( xTypeDetection
->getByName( aDocType
) );
667 aType
= aTypeProps
.getUnpackedValueOrDefault(
671 catch( uno::Exception
& )
675 if ( aTitle
.isEmpty() )
677 INetURLObject
aURL( rURL
);
679 aTitle
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true,
680 INetURLObject::DecodeMechanism::WithCharset
);
687 bool SfxDocTplService_Impl::addEntry( Content
& rParentFolder
,
688 const OUString
& rTitle
,
689 const OUString
& rTargetURL
,
690 const OUString
& rType
)
692 bool bAddedEntry
= false;
694 INetURLObject
aLinkObj( rParentFolder
.getURL() );
695 aLinkObj
.insertName( rTitle
, false,
696 INetURLObject::LAST_SEGMENT
,
697 INetURLObject::EncodeMechanism::All
);
698 const OUString aLinkURL
{aLinkObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
702 if ( ! Content::create( aLinkURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aLink
) )
704 Sequence
< Any
> aValues
{ Any(rTitle
), Any(false), Any(rTargetURL
) };
708 rParentFolder
.insertNewContent( TYPE_LINK
, { TITLE
, IS_FOLDER
, TARGET_URL
}, aValues
, aLink
);
709 setProperty( aLink
, PROPERTY_TYPE
, makeAny( rType
) );
719 bool SfxDocTplService_Impl::createFolder( const OUString
& rNewFolderURL
,
722 Content
&rNewFolder
)
725 bool bCreatedFolder
= false;
726 INetURLObject
aParentURL( rNewFolderURL
);
727 const OUString aFolderName
{aParentURL
.getName( INetURLObject::LAST_SEGMENT
, true,
728 INetURLObject::DecodeMechanism::WithCharset
)};
730 // compute the parent folder url from the new folder url
731 // and remove the final slash, because Content::create doesn't
733 aParentURL
.removeSegment();
734 if ( aParentURL
.getSegmentCount() >= 1 )
735 aParentURL
.removeFinalSlash();
737 // if the parent exists, we can continue with the creation of the
738 // new folder, we have to create the parent otherwise ( as long as
739 // bCreateParent is set to true )
740 if ( Content::create( aParentURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), maCmdEnv
, comphelper::getProcessComponentContext(), aParent
) )
744 Sequence
< Any
> aValues
{ Any(aFolderName
), Any(true) };
748 aType
= TYPE_FSYS_FOLDER
;
752 aParent
.insertNewContent( aType
, { TITLE
, IS_FOLDER
}, aValues
, rNewFolder
);
753 bCreatedFolder
= true;
755 catch( Exception
const & )
757 TOOLS_WARN_EXCEPTION( "sfx.doc", "createFolder(): Could not create new folder" );
760 else if ( bCreateParent
)
762 // if the parent doesn't exists and bCreateParent is set to true,
763 // we try to create the parent and if this was successful, we
764 // try to create the new folder again ( but this time, we set
765 // bCreateParent to false to avoid endless recursions )
766 if ( ( aParentURL
.getSegmentCount() >= 1 ) &&
767 createFolder( aParentURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), bCreateParent
, bFsysFolder
, aParent
) )
769 bCreatedFolder
= createFolder( rNewFolderURL
, false, bFsysFolder
, rNewFolder
);
773 return bCreatedFolder
;
777 bool SfxDocTplService_Impl::CreateNewUniqueFolderWithPrefix( const OUString
& aPath
,
778 const OUString
& aPrefix
,
779 OUString
& aNewFolderName
,
780 OUString
& aNewFolderURL
,
781 Content
& aNewFolder
)
783 bool bCreated
= false;
784 INetURLObject
aDirPath( aPath
);
787 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
788 if ( Content::create( aDirPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), aQuietEnv
, comphelper::getProcessComponentContext(), aParent
) )
790 for ( sal_Int32 nInd
= 0; nInd
< 32000; nInd
++ )
792 OUString aTryName
= aPrefix
;
794 aTryName
+= OUString::number( nInd
);
798 Sequence
< Any
> aValues
{ Any(aTryName
), Any(true) };
799 bCreated
= aParent
.insertNewContent( TYPE_FSYS_FOLDER
, { TITLE
, IS_FOLDER
}, aValues
, aNewFolder
);
801 catch( ucb::NameClashException
& )
803 // if there is already an element, retry
807 INetURLObject
aObjPath( aDirPath
);
808 aObjPath
.insertName( aTryName
, false,
809 INetURLObject::LAST_SEGMENT
,
810 INetURLObject::EncodeMechanism::All
);
811 // if there is already an element, retry
812 // if there was another error, do not try any more
813 if ( !::utl::UCBContentHelper::Exists( aObjPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
819 aNewFolderName
= aTryName
;
820 aNewFolderURL
= aNewFolder
.get()->getIdentifier()->getContentIdentifier();
830 OUString
SfxDocTplService_Impl::CreateNewUniqueFileWithPrefix( const OUString
& aPath
,
831 const OUString
& aPrefix
,
832 const OUString
& aExt
)
834 OUString aNewFileURL
;
835 INetURLObject
aDirPath( aPath
);
839 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
840 if ( Content::create( aDirPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), aQuietEnv
, comphelper::getProcessComponentContext(), aParent
) )
842 for ( sal_Int32 nInd
= 0; nInd
< 32000; nInd
++ )
845 bool bCreated
= false;
846 OUString aTryName
= aPrefix
;
848 aTryName
+= OUString::number( nInd
);
849 if ( aExt
.toChar() != '.' )
855 Sequence
< Any
> aValues
{ Any(aTryName
), Any(true) };
856 bCreated
= aParent
.insertNewContent( TYPE_FSYS_FILE
, { TITLE
, IS_DOCUMENT
}, aValues
, aNewFile
);
858 catch( ucb::NameClashException
& )
860 // if there is already an element, retry
864 INetURLObject
aObjPath( aPath
);
865 aObjPath
.insertName( aTryName
, false,
866 INetURLObject::LAST_SEGMENT
,
867 INetURLObject::EncodeMechanism::All
);
868 // if there is already an element, retry
869 // if there was another error, do not try any more
870 if ( !::utl::UCBContentHelper::Exists( aObjPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
876 aNewFileURL
= aNewFile
.get()->getIdentifier()->getContentIdentifier();
886 bool SfxDocTplService_Impl::removeContent( Content
& rContent
)
888 bool bRemoved
= false;
891 Any aArg
= makeAny( true );
893 rContent
.executeCommand( COMMAND_DELETE
, aArg
);
896 catch ( RuntimeException
& ) {}
897 catch ( Exception
& ) {}
903 bool SfxDocTplService_Impl::removeContent( const OUString
& rContentURL
)
907 if ( Content::create( rContentURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aContent
) )
908 return removeContent( aContent
);
913 bool SfxDocTplService_Impl::setProperty( Content
& rContent
,
914 const OUString
& rPropName
,
915 const Any
& rPropValue
)
917 bool bPropertySet
= false;
919 // Store the property
922 Any
aPropValue( rPropValue
);
923 uno::Reference
< XPropertySetInfo
> aPropInfo
= rContent
.getProperties();
925 // check, whether or not the property exists, create it, when not
926 if ( !aPropInfo
.is() || !aPropInfo
->hasPropertyByName( rPropName
) )
928 uno::Reference
< XPropertyContainer
> xProperties( rContent
.get(), UNO_QUERY
);
929 if ( xProperties
.is() )
933 xProperties
->addProperty( rPropName
, PropertyAttribute::MAYBEVOID
, rPropValue
);
935 catch( PropertyExistException
& ) {}
936 catch( IllegalTypeException
& ) {
937 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
939 catch( IllegalArgumentException
& ) {
940 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
945 // To ensure a reloctable office installation, the path to the
946 // office installation directory must never be stored directly.
947 if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName
) )
950 if ( rPropValue
>>= aValue
)
952 maRelocator
.makeRelocatableURL( aValue
);
953 aPropValue
<<= aValue
;
957 Sequence
< OUString
> aValues
;
958 if ( rPropValue
>>= aValues
)
960 for ( auto& rValue
: asNonConstRange(aValues
) )
962 maRelocator
.makeRelocatableURL( rValue
);
964 aPropValue
<<= aValues
;
968 OSL_FAIL( "Unsupported property value type" );
973 // now set the property
975 rContent
.setPropertyValue( rPropName
, aPropValue
);
978 catch ( RuntimeException
& ) {}
979 catch ( Exception
& ) {}
985 bool SfxDocTplService_Impl::getProperty(Content
& rContent
, const OUString
& rPropName
, Any
& rPropValue
)
987 bool bGotProperty
= false;
992 uno::Reference
< XPropertySetInfo
> aPropInfo
= rContent
.getProperties();
994 // check, whether or not the property exists
995 if ( !aPropInfo
.is() || !aPropInfo
->hasPropertyByName( rPropName
) )
1000 // now get the property
1002 rPropValue
= rContent
.getPropertyValue( rPropName
);
1004 // To ensure a reloctable office installation, the path to the
1005 // office installation directory must never be stored directly.
1006 if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName
) )
1009 if ( rPropValue
>>= aValue
)
1011 maRelocator
.makeAbsoluteURL( aValue
);
1012 rPropValue
<<= aValue
;
1016 Sequence
< OUString
> aValues
;
1017 if ( rPropValue
>>= aValues
)
1019 for ( auto& rValue
: asNonConstRange(aValues
) )
1021 maRelocator
.makeAbsoluteURL( rValue
);
1023 rPropValue
<<= aValues
;
1027 OSL_FAIL( "Unsupported property value type" );
1032 bGotProperty
= true;
1034 catch ( RuntimeException
& ) {}
1035 catch ( Exception
& ) {}
1037 return bGotProperty
;
1040 SfxDocTplService_Impl::SfxDocTplService_Impl( const uno::Reference
< XComponentContext
> & xContext
)
1041 : mxContext(xContext
), mbIsInitialized(false), mbLocaleSet(false), maRelocator(xContext
)
1046 SfxDocTplService_Impl::~SfxDocTplService_Impl()
1048 ::osl::MutexGuard
aGuard( maMutex
);
1053 lang::Locale
SfxDocTplService_Impl::getLocale()
1055 ::osl::MutexGuard
aGuard( maMutex
);
1064 void SfxDocTplService_Impl::setLocale( const lang::Locale
&rLocale
)
1066 ::osl::MutexGuard
aGuard( maMutex
);
1068 if ( mbLocaleSet
&& (
1069 ( maLocale
.Language
!= rLocale
.Language
) ||
1070 ( maLocale
.Country
!= rLocale
.Country
) ||
1071 ( maLocale
.Variant
!= rLocale
.Variant
) ) )
1072 mbIsInitialized
= false;
1079 void SfxDocTplService_Impl::update()
1081 ::osl::MutexGuard
aGuard( maMutex
);
1087 void SfxDocTplService_Impl::doUpdate()
1089 ::osl::MutexGuard
aGuard( maMutex
);
1091 const OUString
aPropName( PROPERTY_NEEDSUPDATE
);
1095 setProperty( maRootContent
, aPropName
, aValue
);
1097 GroupList_Impl aGroupList
;
1099 // get the entries from the hierarchy
1100 createFromContent( aGroupList
, maRootContent
, true, false );
1102 // get the entries from the template directories
1103 sal_Int32 nCountDir
= maTemplateDirs
.getLength();
1104 const OUString
* pDirs
= maTemplateDirs
.getConstArray();
1105 Content aDirContent
;
1107 // the last directory in the list must be writable
1108 bool bWriteableDirectory
= true;
1110 // the target folder might not exist, for this reason no interaction handler should be used
1111 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
1116 if ( Content::create( pDirs
[ nCountDir
], aQuietEnv
, comphelper::getProcessComponentContext(), aDirContent
) )
1118 createFromContent( aGroupList
, aDirContent
, false, bWriteableDirectory
);
1121 bWriteableDirectory
= false;
1124 // now check the list
1125 for(std::unique_ptr
<GroupData_Impl
>& pGroup
: aGroupList
)
1127 if ( pGroup
->getInUse() )
1129 if ( pGroup
->getInHierarchy() )
1132 if ( Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1133 setProperty( aGroup
,
1135 makeAny( pGroup
->getTargetURL() ) );
1137 size_t nCount
= pGroup
->count();
1138 for ( size_t i
=0; i
<nCount
; i
++ )
1140 DocTemplates_EntryData_Impl
*pData
= pGroup
->getEntry( i
);
1141 if ( ! pData
->getInUse() )
1143 if ( pData
->getInHierarchy() )
1144 removeFromHierarchy( pData
); // delete entry in hierarchy
1146 addToHierarchy( pGroup
.get(), pData
); // add entry to hierarchy
1148 else if ( pData
->getUpdateType() ||
1149 pData
->getUpdateLink() )
1151 updateData( pData
);
1157 addGroupToHierarchy( pGroup
.get() ); // add group to hierarchy
1161 removeFromHierarchy( pGroup
.get() ); // delete group from hierarchy
1166 setProperty( maRootContent
, aPropName
, aValue
);
1170 std::vector
< beans::StringPair
> SfxDocTplService_Impl::ReadUINamesForTemplateDir_Impl( const OUString
& aUserPath
)
1172 INetURLObject
aLocObj( aUserPath
);
1173 aLocObj
.insertName( u
"groupuinames.xml", false,
1174 INetURLObject::LAST_SEGMENT
,
1175 INetURLObject::EncodeMechanism::All
);
1176 Content aLocContent
;
1178 // TODO/LATER: Use hashmap in future
1179 std::vector
< beans::StringPair
> aUINames
;
1180 if ( Content::create( aLocObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), uno::Reference
< ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext(), aLocContent
) )
1184 uno::Reference
< io::XInputStream
> xLocStream
= aLocContent
.openStream();
1185 if ( xLocStream
.is() )
1186 aUINames
= DocTemplLocaleHelper::ReadGroupLocalizationSequence( xLocStream
, mxContext
);
1188 catch( uno::Exception
& )
1196 bool SfxDocTplService_Impl::UpdateUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
1197 const OUString
& aGroupName
,
1198 const OUString
& aNewFolderName
)
1200 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1201 sal_Int32 nLen
= aUINames
.size();
1203 // it is possible that the name is used already, but it should be checked before
1204 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1205 if ( aUINames
[nInd
].First
== aNewFolderName
)
1208 aUINames
.resize( ++nLen
);
1209 aUINames
[nLen
-1].First
= aNewFolderName
;
1210 aUINames
[nLen
-1].Second
= aGroupName
;
1212 return WriteUINamesForTemplateDir_Impl( aUserPath
, aUINames
);
1216 bool SfxDocTplService_Impl::ReplaceUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
1217 const OUString
& aDefaultFsysGroupName
,
1218 std::u16string_view aOldGroupName
,
1219 const OUString
& aNewGroupName
)
1221 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1222 sal_Int32 nLen
= aUINames
.size();
1224 bool bChanged
= false;
1225 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1226 if ( aUINames
[nInd
].Second
== aOldGroupName
)
1228 aUINames
[nInd
].Second
= aNewGroupName
;
1234 aUINames
.resize( ++nLen
);
1235 aUINames
[nLen
-1].First
= aDefaultFsysGroupName
;
1236 aUINames
[nLen
-1].Second
= aNewGroupName
;
1238 return WriteUINamesForTemplateDir_Impl( aUserPath
, aUINames
);
1242 void SfxDocTplService_Impl::RemoveUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
1243 std::u16string_view aGroupName
)
1245 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1246 sal_Int32 nLen
= aUINames
.size();
1247 std::vector
< beans::StringPair
> aNewUINames( nLen
);
1248 sal_Int32 nNewLen
= 0;
1250 bool bChanged
= false;
1251 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1252 if ( aUINames
[nInd
].Second
== aGroupName
)
1257 aNewUINames
[nNewLen
-1].First
= aUINames
[nInd
].First
;
1258 aNewUINames
[nNewLen
-1].Second
= aUINames
[nInd
].Second
;
1261 aNewUINames
.resize( nNewLen
);
1264 WriteUINamesForTemplateDir_Impl( aUserPath
, aNewUINames
);
1268 bool SfxDocTplService_Impl::WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
1269 const std::vector
< beans::StringPair
>& aUINames
)
1271 bool bResult
= false;
1273 uno::Reference
< io::XTempFile
> xTempFile(
1274 io::TempFile::create(mxContext
),
1275 uno::UNO_SET_THROW
);
1277 uno::Reference
< io::XOutputStream
> xOutStream
= xTempFile
->getOutputStream();
1278 if ( !xOutStream
.is() )
1279 throw uno::RuntimeException();
1281 DocTemplLocaleHelper::WriteGroupLocalizationSequence( xOutStream
, aUINames
, mxContext
);
1283 // the SAX writer might close the stream
1284 xOutStream
->closeOutput();
1285 } catch( uno::Exception
& )
1288 uno::Reference
< ucb::XSimpleFileAccess3
> xAccess(ucb::SimpleFileAccess::create(mxContext
));
1289 xAccess
->writeFile(OUString::Concat(aUserPath
) + "groupuinames.xml", xTempFile
->getInputStream());
1293 catch ( uno::Exception
& )
1295 TOOLS_WARN_EXCEPTION("sfx.doc", "");
1302 OUString
SfxDocTplService_Impl::CreateNewGroupFsys( const OUString
& rGroupName
, Content
& aGroup
)
1304 OUString aResultURL
;
1306 if ( maTemplateDirs
.hasElements() )
1308 OUString aTargetPath
= maTemplateDirs
[ maTemplateDirs
.getLength() - 1 ];
1310 // create a new folder with the given name
1312 OUString aNewFolderName
;
1314 // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1315 if ( !CreateNewUniqueFolderWithPrefix( aTargetPath
,
1320 && !CreateNewUniqueFolderWithPrefix( aTargetPath
,
1328 if ( !UpdateUINamesForTemplateDir_Impl( aTargetPath
, rGroupName
, aNewFolderName
) )
1330 // we could not create the groupuinames for the folder, so we delete the group in
1331 // the folder and return
1332 removeContent( aNewFolder
);
1336 // Now set the target url for this group and we are done
1337 Any aValue
= makeAny( aResultURL
);
1339 if ( ! setProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1341 removeContent( aNewFolder
);
1350 bool SfxDocTplService_Impl::addGroup( const OUString
& rGroupName
)
1352 ::osl::MutexGuard
aGuard( maMutex
);
1354 // Check, whether or not there is a group with this name
1356 OUString aNewGroupURL
;
1357 INetURLObject
aNewGroupObj( maRootURL
);
1359 aNewGroupObj
.insertName( rGroupName
, false,
1360 INetURLObject::LAST_SEGMENT
,
1361 INetURLObject::EncodeMechanism::All
);
1363 aNewGroupURL
= aNewGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1365 if ( Content::create( aNewGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aNewGroup
) ||
1366 ! createFolder( aNewGroupURL
, false, false, aNewGroup
) )
1368 // if there already was a group with this name or the new group
1369 // could not be created, we return here
1373 // Get the user template path entry ( new group will always
1374 // be added in the user template path )
1378 nIndex
= maTemplateDirs
.getLength();
1382 return false; // We don't know where to add the group
1384 aUserPath
= maTemplateDirs
[ nIndex
];
1386 // create a new folder with the given name
1388 OUString aNewFolderName
;
1389 OUString aNewFolderURL
;
1391 // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1392 if ( !CreateNewUniqueFolderWithPrefix( aUserPath
,
1397 && !CreateNewUniqueFolderWithPrefix( aUserPath
,
1403 // we could not create the folder, so we delete the group in the
1404 // hierarchy and return
1405 removeContent( aNewGroup
);
1409 if ( !UpdateUINamesForTemplateDir_Impl( aUserPath
, rGroupName
, aNewFolderName
) )
1411 // we could not create the groupuinames for the folder, so we delete the group in the
1412 // hierarchy, the folder and return
1413 removeContent( aNewGroup
);
1414 removeContent( aNewFolder
);
1418 // Now set the target url for this group and we are done
1419 Any aValue
= makeAny( aNewFolderURL
);
1421 if ( ! setProperty( aNewGroup
, TARGET_DIR_URL
, aValue
) )
1423 removeContent( aNewGroup
);
1424 removeContent( aNewFolder
);
1432 bool SfxDocTplService_Impl::removeGroup( std::u16string_view rGroupName
)
1434 // remove all the elements that have the prefix aTargetURL
1435 // if the group does not have other elements remove it
1437 ::osl::MutexGuard
aGuard( maMutex
);
1439 bool bResult
= false;
1441 // create the group url
1442 INetURLObject
aGroupObj( maRootURL
);
1443 aGroupObj
.insertName( rGroupName
, false,
1444 INetURLObject::LAST_SEGMENT
,
1445 INetURLObject::EncodeMechanism::All
);
1447 // Get the target url
1449 const OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1451 if ( Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1453 const OUString
aPropName( TARGET_DIR_URL
);
1456 OUString aGroupTargetURL
;
1457 if ( getProperty( aGroup
, aPropName
, aValue
) )
1458 aValue
>>= aGroupTargetURL
;
1460 if ( aGroupTargetURL
.isEmpty() )
1461 return false; // nothing is allowed to be removed
1463 if ( !maTemplateDirs
.hasElements() )
1466 // check that the fs location is in writable folder and this is not a "My templates" folder
1467 INetURLObject
aGroupParentFolder( aGroupTargetURL
);
1468 if (!aGroupParentFolder
.removeSegment())
1471 OUString aGeneralTempPath
= findParentTemplateDir(
1472 aGroupParentFolder
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1474 if (aGeneralTempPath
.isEmpty())
1477 // now get the content of the Group
1478 uno::Reference
< XResultSet
> xResultSet
;
1479 Sequence
< OUString
> aProps
{ TARGET_URL
};
1483 xResultSet
= aGroup
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
1485 if ( xResultSet
.is() )
1487 bool bHasNonRemovable
= false;
1488 bool bHasShared
= false;
1490 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY_THROW
);
1491 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY_THROW
);
1493 while ( xResultSet
->next() )
1495 OUString
aTemplTargetURL( xRow
->getString( 1 ) );
1496 OUString aHierURL
= xContentAccess
->queryContentIdentifierString();
1498 if ( ::utl::UCBContentHelper::IsSubPath( aGroupTargetURL
, aTemplTargetURL
) )
1500 // this is a user template, and it can be removed
1501 if ( removeContent( aTemplTargetURL
) )
1502 removeContent( aHierURL
);
1504 bHasNonRemovable
= true;
1510 if ( !bHasNonRemovable
&& !bHasShared
)
1512 if ( removeContent( aGroupTargetURL
)
1513 || !::utl::UCBContentHelper::Exists( aGroupTargetURL
) )
1515 removeContent( aGroupURL
);
1516 RemoveUINamesForTemplateDir_Impl( aGeneralTempPath
, rGroupName
);
1517 bResult
= true; // the operation is successful only if the whole group is removed
1520 else if ( !bHasNonRemovable
)
1522 if ( removeContent( aGroupTargetURL
)
1523 || !::utl::UCBContentHelper::Exists( aGroupTargetURL
) )
1525 RemoveUINamesForTemplateDir_Impl( aGeneralTempPath
, rGroupName
);
1526 setProperty( aGroup
, aPropName
, uno::makeAny( OUString() ) );
1531 catch ( Exception
& ) {}
1538 bool SfxDocTplService_Impl::renameGroup( std::u16string_view rOldName
,
1539 const OUString
& rNewName
)
1541 ::osl::MutexGuard
aGuard( maMutex
);
1543 // create the group url
1545 INetURLObject
aGroupObj( maRootURL
);
1546 aGroupObj
.insertName( rNewName
, false,
1547 INetURLObject::LAST_SEGMENT
,
1548 INetURLObject::EncodeMechanism::All
);
1549 OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1551 // Check, if there is a group with the new name, return false
1553 if ( Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1556 aGroupObj
.removeSegment();
1557 aGroupObj
.insertName( rOldName
, false,
1558 INetURLObject::LAST_SEGMENT
,
1559 INetURLObject::EncodeMechanism::All
);
1560 aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1562 // When there is no group with the old name, we can't rename it
1563 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1566 OUString aGroupTargetURL
;
1567 // there is no need to check whether target dir url is in target path, since if the target path is changed
1568 // the target dir url should be already generated new
1570 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1571 aValue
>>= aGroupTargetURL
;
1573 if ( aGroupTargetURL
.isEmpty() )
1576 if ( !maTemplateDirs
.hasElements() )
1579 // check that the fs location is in writable folder and this is not a "My templates" folder
1580 INetURLObject
aGroupParentFolder( aGroupTargetURL
);
1581 if (!aGroupParentFolder
.removeSegment() ||
1582 isInternalTemplateDir(aGroupParentFolder
.GetMainURL(INetURLObject::DecodeMechanism::NONE
)))
1587 // check that the group can be renamed ( all the contents must be in target location )
1588 bool bCanBeRenamed
= false;
1591 uno::Reference
< XResultSet
> xResultSet
;
1592 Sequence
< OUString
> aProps
{ TARGET_URL
};
1593 xResultSet
= aGroup
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
1595 if ( xResultSet
.is() )
1597 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY_THROW
);
1598 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY_THROW
);
1600 while ( xResultSet
->next() )
1602 if ( !::utl::UCBContentHelper::IsSubPath( aGroupTargetURL
, xRow
->getString( 1 ) ) )
1603 throw uno::Exception("not sub path", nullptr);
1606 bCanBeRenamed
= true;
1609 catch ( Exception
& ) {}
1611 if ( bCanBeRenamed
)
1613 INetURLObject
aGroupTargetObj( aGroupTargetURL
);
1614 const OUString aFsysName
= aGroupTargetObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
1616 if ( aGroupTargetObj
.removeSegment()
1617 && ReplaceUINamesForTemplateDir_Impl( aGroupTargetObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
1622 // rename the group in the hierarchy
1624 aTitleValue
<<= rNewName
;
1626 return setProperty( aGroup
, TITLE
, aTitleValue
);
1634 bool SfxDocTplService_Impl::storeTemplate( const OUString
& rGroupName
,
1635 const OUString
& rTemplateName
,
1636 const uno::Reference
< frame::XStorable
>& rStorable
)
1638 ::osl::MutexGuard
aGuard( maMutex
);
1640 // Check, whether or not there is a group with this name
1641 // Return false, if there is no group with the given name
1642 Content aGroup
, aTemplateToRemove
;
1643 INetURLObject
aGroupObj( maRootURL
);
1644 bool bRemoveOldTemplateContent
= false;
1646 aGroupObj
.insertName( rGroupName
, false,
1647 INetURLObject::LAST_SEGMENT
,
1648 INetURLObject::EncodeMechanism::All
);
1649 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1651 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1654 OUString aGroupTargetURL
;
1656 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1657 aValue
>>= aGroupTargetURL
;
1660 // Check, if there's a template with the given name in this group
1661 // the target template should be overwritten if it is imported by user
1662 // in case the template is installed by office installation of by an add-in
1663 // it can not be replaced
1664 aGroupObj
.insertName( rTemplateName
, false,
1665 INetURLObject::LAST_SEGMENT
,
1666 INetURLObject::EncodeMechanism::All
);
1667 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1669 OUString aTemplateToRemoveTargetURL
;
1671 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplateToRemove
) )
1673 bRemoveOldTemplateContent
= true;
1674 if ( getProperty( aTemplateToRemove
, TARGET_URL
, aValue
) )
1675 aValue
>>= aTemplateToRemoveTargetURL
;
1677 if ( aGroupTargetURL
.isEmpty() || !maTemplateDirs
.hasElements()
1678 || (!aTemplateToRemoveTargetURL
.isEmpty() && isInternalTemplateDir(aTemplateToRemoveTargetURL
)) )
1679 return false; // it is not allowed to remove the template
1684 uno::Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
1686 // get document service name
1687 uno::Reference
< frame::XModuleManager2
> xModuleManager( frame::ModuleManager::create(xContext
) );
1688 const OUString sDocServiceName
{xModuleManager
->identify( uno::Reference
< uno::XInterface
>( rStorable
, uno::UNO_QUERY
) )};
1689 if ( sDocServiceName
.isEmpty() )
1690 throw uno::RuntimeException();
1692 // get the actual filter name
1693 uno::Reference
< lang::XMultiServiceFactory
> xConfigProvider
=
1694 configuration::theDefaultProvider::get( xContext
);
1696 uno::Sequence
<uno::Any
> aArgs(comphelper::InitAnyPropertySequence(
1698 {"nodepath", uno::Any(OUString( "/org.openoffice.Setup/Office/Factories/" ))}
1700 uno::Reference
< container::XNameAccess
> xSOFConfig(
1701 xConfigProvider
->createInstanceWithArguments(
1702 "com.sun.star.configuration.ConfigurationAccess",
1704 uno::UNO_QUERY_THROW
);
1706 uno::Reference
< container::XNameAccess
> xApplConfig
;
1707 xSOFConfig
->getByName( sDocServiceName
) >>= xApplConfig
;
1708 if ( !xApplConfig
.is() )
1709 throw uno::RuntimeException();
1711 OUString aFilterName
;
1712 xApplConfig
->getByName("ooSetupFactoryActualTemplateFilter") >>= aFilterName
;
1713 if ( aFilterName
.isEmpty() )
1714 throw uno::RuntimeException();
1716 // find the related type name
1717 uno::Reference
< container::XNameAccess
> xFilterFactory(
1718 mxContext
->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", mxContext
),
1719 uno::UNO_QUERY_THROW
);
1721 uno::Sequence
< beans::PropertyValue
> aFilterData
;
1722 xFilterFactory
->getByName( aFilterName
) >>= aFilterData
;
1724 for ( const auto& rProp
: std::as_const(aFilterData
) )
1725 if ( rProp
.Name
== "Type" )
1726 rProp
.Value
>>= aTypeName
;
1728 if ( aTypeName
.isEmpty() )
1729 throw uno::RuntimeException();
1731 // find the mediatype and extension
1732 uno::Reference
< container::XNameAccess
> xTypeDetection
=
1734 uno::Reference
< container::XNameAccess
>( mxType
, uno::UNO_QUERY_THROW
) :
1735 uno::Reference
< container::XNameAccess
>(
1736 mxContext
->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", mxContext
),
1737 uno::UNO_QUERY_THROW
);
1739 SequenceAsHashMap
aTypeProps( xTypeDetection
->getByName( aTypeName
) );
1740 uno::Sequence
< OUString
> aAllExt
=
1741 aTypeProps
.getUnpackedValueOrDefault("Extensions", Sequence
< OUString
>() );
1742 if ( !aAllExt
.hasElements() )
1743 throw uno::RuntimeException();
1745 const OUString aMediaType
{aTypeProps
.getUnpackedValueOrDefault("MediaType", OUString() )};
1746 const OUString aExt
{aAllExt
[0]};
1748 if ( aMediaType
.isEmpty() || aExt
.isEmpty() )
1749 throw uno::RuntimeException();
1751 // construct destination url
1752 if ( aGroupTargetURL
.isEmpty() )
1754 aGroupTargetURL
= CreateNewGroupFsys( rGroupName
, aGroup
);
1756 if ( aGroupTargetURL
.isEmpty() )
1757 throw uno::RuntimeException();
1760 OUString aNewTemplateTargetURL
= CreateNewUniqueFileWithPrefix( aGroupTargetURL
, rTemplateName
, aExt
);
1761 if ( aNewTemplateTargetURL
.isEmpty() )
1763 aNewTemplateTargetURL
= CreateNewUniqueFileWithPrefix( aGroupTargetURL
, "UserTemplate", aExt
);
1765 if ( aNewTemplateTargetURL
.isEmpty() )
1766 throw uno::RuntimeException();
1770 uno::Sequence
< PropertyValue
> aStoreArgs
{
1771 comphelper::makePropertyValue("FilterName", aFilterName
),
1772 comphelper::makePropertyValue("DocumentTitle", rTemplateName
)
1775 if( !::utl::UCBContentHelper::EqualURLs( aNewTemplateTargetURL
, rStorable
->getLocation() ))
1776 rStorable
->storeToURL( aNewTemplateTargetURL
, aStoreArgs
);
1780 // the storing was successful, now the old template with the same name can be removed if it existed
1781 if ( !aTemplateToRemoveTargetURL
.isEmpty() )
1783 removeContent( aTemplateToRemoveTargetURL
);
1787 * if the old template was the standard template
1788 * it is necessary to change the standard template with the new file name
1790 const OUString sStdTmplFile
= SfxObjectFactory::GetStandardTemplate( sDocServiceName
);
1791 if ( INetURLObject( sStdTmplFile
) == INetURLObject( aTemplateToRemoveTargetURL
) )
1793 SfxObjectFactory::SetStandardTemplate( sDocServiceName
, aNewTemplateTargetURL
);
1797 if ( bRemoveOldTemplateContent
)
1798 removeContent( aTemplateToRemove
);
1800 // add the template to hierarchy
1801 return addEntry( aGroup
, rTemplateName
, aNewTemplateTargetURL
, aMediaType
);
1805 // the template was not stored
1811 bool SfxDocTplService_Impl::addTemplate( const OUString
& rGroupName
,
1812 const OUString
& rTemplateName
,
1813 const OUString
& rSourceURL
)
1815 ::osl::MutexGuard
aGuard( maMutex
);
1817 // Check, whether or not there is a group with this name
1818 // Return false, if there is no group with the given name
1819 Content aGroup
, aTemplate
, aTargetGroup
;
1820 INetURLObject
aGroupObj( maRootURL
);
1822 aGroupObj
.insertName( rGroupName
, false,
1823 INetURLObject::LAST_SEGMENT
,
1824 INetURLObject::EncodeMechanism::All
);
1825 const OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1827 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1830 // Check, if there's a template with the given name in this group
1831 // Return false, if there already is a template
1832 aGroupObj
.insertName( rTemplateName
, false,
1833 INetURLObject::LAST_SEGMENT
,
1834 INetURLObject::EncodeMechanism::All
);
1835 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1837 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
1840 // get the target url of the group
1841 OUString aTargetURL
;
1844 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1845 aValue
>>= aTargetURL
;
1847 if ( aTargetURL
.isEmpty() )
1849 aTargetURL
= CreateNewGroupFsys( rGroupName
, aGroup
);
1851 if ( aTargetURL
.isEmpty() )
1855 // Get the content type
1856 OUString aTitle
, aType
;
1858 bool bDocHasTitle
= false;
1859 getTitleFromURL( rSourceURL
, aTitle
, aType
, bDocHasTitle
);
1861 INetURLObject
aSourceObj( rSourceURL
);
1862 if ( rTemplateName
== aTitle
)
1864 // addTemplate will sometimes be called just to add an entry in the
1865 // hierarchy; the target URL and the source URL will be the same in
1867 // TODO/LATER: get rid of this old hack
1869 INetURLObject
aTargetObj( aTargetURL
);
1871 aTargetObj
.insertName( rTemplateName
, false,
1872 INetURLObject::LAST_SEGMENT
,
1873 INetURLObject::EncodeMechanism::All
);
1874 aTargetObj
.setExtension( aSourceObj
.getExtension() );
1876 const OUString aTargetURL2
= aTargetObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1878 if ( aTargetURL2
== rSourceURL
)
1879 return addEntry( aGroup
, rTemplateName
, aTargetURL2
, aType
);
1882 // copy the template into the new group (targeturl)
1884 INetURLObject
aTmpURL( aSourceObj
);
1885 aTmpURL
.CutExtension();
1886 const OUString aPattern
{aTmpURL
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
)};
1888 const OUString aNewTemplateTargetURL
{CreateNewUniqueFileWithPrefix( aTargetURL
, aPattern
, aSourceObj
.getExtension() )};
1889 INetURLObject
aNewTemplateTargetObj( aNewTemplateTargetURL
);
1890 const OUString aNewTemplateTargetName
{aNewTemplateTargetObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
)};
1891 if ( aNewTemplateTargetURL
.isEmpty() || aNewTemplateTargetName
.isEmpty() )
1894 // get access to source file
1895 Content aSourceContent
;
1896 uno::Reference
< ucb::XCommandEnvironment
> xEnv
;
1897 INetURLObject
aSourceURL( rSourceURL
);
1898 if( ! Content::create( aSourceURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext(), aSourceContent
) )
1901 if( ! Content::create( aTargetURL
, xEnv
, comphelper::getProcessComponentContext(), aTargetGroup
) )
1904 // transfer source file
1907 aTargetGroup
.transferContent( aSourceContent
,
1908 InsertOperation::Copy
,
1909 aNewTemplateTargetName
,
1910 NameClash::OVERWRITE
,
1913 // allow to edit the added template
1914 Content aResultContent
;
1915 if ( Content::create( aNewTemplateTargetURL
, xEnv
, comphelper::getProcessComponentContext(), aResultContent
) )
1917 static const OUStringLiteral
aPropertyName( u
"IsReadOnly" );
1919 bool bReadOnly
= false;
1920 if ( getProperty( aResultContent
, aPropertyName
, aProperty
) && ( aProperty
>>= bReadOnly
) && bReadOnly
)
1921 setProperty( aResultContent
, aPropertyName
, uno::makeAny( false ) );
1924 catch ( ContentCreationException
& )
1926 catch ( Exception
& )
1930 // either the document has title and it is the same as requested, or we have to set it
1931 bool bCorrectTitle
= ( bDocHasTitle
&& aTitle
== rTemplateName
);
1932 if ( !bCorrectTitle
)
1934 if ( !bDocHasTitle
)
1936 INetURLObject
aNewTmpObj( aNewTemplateTargetObj
);
1937 aNewTmpObj
.CutExtension();
1938 bCorrectTitle
= ( aNewTmpObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
) == rTemplateName
);
1941 if ( !bCorrectTitle
)
1942 bCorrectTitle
= setTitleForURL( aNewTemplateTargetURL
, rTemplateName
);
1945 if ( bCorrectTitle
)
1947 // create a new entry in the hierarchy
1948 return addEntry( aGroup
, rTemplateName
, aNewTemplateTargetURL
, aType
);
1951 // TODO/LATER: The user could be notified here that the renaming has failed
1952 // create a new entry in the hierarchy
1953 addEntry( aGroup
, aTitle
, aNewTemplateTargetURL
, aType
);
1957 bool SfxDocTplService_Impl::isInternalTemplateDir(const OUString
& rURL
) const
1959 return std::any_of(maInternalTemplateDirs
.begin(), maInternalTemplateDirs
.end(),
1960 [&rURL
](const OUString
& rDir
) { return ::utl::UCBContentHelper::IsSubPath(rDir
, rURL
); });
1963 OUString
SfxDocTplService_Impl::findParentTemplateDir(const OUString
& rURL
) const
1965 const OUString
* pDirs
= std::find_if(maTemplateDirs
.begin(), maTemplateDirs
.end(),
1966 [&rURL
](const OUString
& rDir
) { return ::utl::UCBContentHelper::IsSubPath(rDir
, rURL
); });
1967 if (pDirs
!= maTemplateDirs
.end())
1972 bool SfxDocTplService_Impl::removeTemplate( std::u16string_view rGroupName
,
1973 std::u16string_view rTemplateName
)
1975 ::osl::MutexGuard
aGuard( maMutex
);
1977 // Check, whether or not there is a group with this name
1978 // Return false, if there is no group with the given name
1979 Content aGroup
, aTemplate
;
1980 INetURLObject
aGroupObj( maRootURL
);
1982 aGroupObj
.insertName( rGroupName
, false,
1983 INetURLObject::LAST_SEGMENT
,
1984 INetURLObject::EncodeMechanism::All
);
1985 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1987 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1990 // Check, if there's a template with the given name in this group
1991 // Return false, if there is no template
1992 aGroupObj
.insertName( rTemplateName
, false,
1993 INetURLObject::LAST_SEGMENT
,
1994 INetURLObject::EncodeMechanism::All
);
1995 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1997 if ( !Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2000 // get the target URL from the template
2001 OUString aTargetURL
;
2004 if ( getProperty( aTemplate
, TARGET_URL
, aValue
) )
2005 aValue
>>= aTargetURL
;
2007 // delete the target template
2008 if ( !aTargetURL
.isEmpty() )
2010 if (isInternalTemplateDir(aTargetURL
))
2013 removeContent( aTargetURL
);
2016 // delete the template entry
2017 return removeContent( aTemplate
);
2021 bool SfxDocTplService_Impl::renameTemplate( std::u16string_view rGroupName
,
2022 std::u16string_view rOldName
,
2023 const OUString
& rNewName
)
2025 ::osl::MutexGuard
aGuard( maMutex
);
2027 // Check, whether or not there is a group with this name
2028 // Return false, if there is no group with the given name
2029 Content aGroup
, aTemplate
;
2030 INetURLObject
aGroupObj( maRootURL
);
2032 aGroupObj
.insertName( rGroupName
, false,
2033 INetURLObject::LAST_SEGMENT
,
2034 INetURLObject::EncodeMechanism::All
);
2035 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2037 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2040 // Check, if there's a template with the new name in this group
2041 // Return false, if there is one
2042 aGroupObj
.insertName( rNewName
, false,
2043 INetURLObject::LAST_SEGMENT
,
2044 INetURLObject::EncodeMechanism::All
);
2045 OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2047 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2050 // Check, if there's a template with the old name in this group
2051 // Return false, if there is no template
2052 aGroupObj
.removeSegment();
2053 aGroupObj
.insertName( rOldName
, false,
2054 INetURLObject::LAST_SEGMENT
,
2055 INetURLObject::EncodeMechanism::All
);
2056 aTemplateURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2058 if ( !Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2061 OUString aTemplateTargetURL
;
2064 if ( getProperty( aTemplate
, TARGET_URL
, aTargetValue
) )
2065 aTargetValue
>>= aTemplateTargetURL
;
2067 if ( !setTitleForURL( aTemplateTargetURL
, rNewName
) )
2070 // rename the template entry in the cache
2072 aTitleValue
<<= rNewName
;
2074 return setProperty( aTemplate
, TITLE
, aTitleValue
);
2078 class SfxDocTplService
: public ::cppu::WeakImplHelper
< css::lang::XLocalizable
, css::frame::XDocumentTemplates
, css::lang::XServiceInfo
>
2080 std::unique_ptr
<SfxDocTplService_Impl
> pImp
;
2083 explicit SfxDocTplService( const css::uno::Reference
< uno::XComponentContext
>& xContext
);
2085 virtual OUString SAL_CALL
getImplementationName() override
2087 return "com.sun.star.comp.sfx2.DocumentTemplates";
2090 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
) override
2092 return cppu::supportsService(this, ServiceName
);
2095 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
2097 css::uno::Sequence
< OUString
> aSeq
{ "com.sun.star.frame.DocumentTemplates" };
2102 // --- XLocalizable ---
2103 void SAL_CALL
setLocale( const css::lang::Locale
& eLocale
) override
;
2104 css::lang::Locale SAL_CALL
getLocale() override
;
2106 // --- XDocumentTemplates ---
2107 css::uno::Reference
< css::ucb::XContent
> SAL_CALL
getContent() override
;
2108 sal_Bool SAL_CALL
storeTemplate( const OUString
& GroupName
,
2109 const OUString
& TemplateName
,
2110 const css::uno::Reference
< css::frame::XStorable
>& Storable
) override
;
2111 sal_Bool SAL_CALL
addTemplate( const OUString
& GroupName
,
2112 const OUString
& TemplateName
,
2113 const OUString
& SourceURL
) override
;
2114 sal_Bool SAL_CALL
removeTemplate( const OUString
& GroupName
,
2115 const OUString
& TemplateName
) override
;
2116 sal_Bool SAL_CALL
renameTemplate( const OUString
& GroupName
,
2117 const OUString
& OldTemplateName
,
2118 const OUString
& NewTemplateName
) override
;
2119 sal_Bool SAL_CALL
addGroup( const OUString
& GroupName
) override
;
2120 sal_Bool SAL_CALL
removeGroup( const OUString
& GroupName
) override
;
2121 sal_Bool SAL_CALL
renameGroup( const OUString
& OldGroupName
,
2122 const OUString
& NewGroupName
) override
;
2123 void SAL_CALL
update() override
;
2127 SfxDocTplService::SfxDocTplService( const uno::Reference
< XComponentContext
>& xContext
)
2129 pImp
.reset( new SfxDocTplService_Impl(xContext
) );
2134 //--- XLocalizable ---
2137 lang::Locale SAL_CALL
SfxDocTplService::getLocale()
2139 return pImp
->getLocale();
2143 void SAL_CALL
SfxDocTplService::setLocale( const lang::Locale
& rLocale
)
2145 pImp
->setLocale( rLocale
);
2149 //--- XDocumentTemplates ---
2151 uno::Reference
< ucb::XContent
> SAL_CALL
SfxDocTplService::getContent()
2154 return pImp
->getContent().get();
2159 sal_Bool SAL_CALL
SfxDocTplService::storeTemplate( const OUString
& GroupName
,
2160 const OUString
& TemplateName
,
2161 const uno::Reference
< frame::XStorable
>& Storable
)
2163 return pImp
->init() && pImp
->storeTemplate( GroupName
, TemplateName
, Storable
);
2167 sal_Bool SAL_CALL
SfxDocTplService::addTemplate( const OUString
& rGroupName
,
2168 const OUString
& rTemplateName
,
2169 const OUString
& rSourceURL
)
2171 return pImp
->init() && pImp
->addTemplate( rGroupName
, rTemplateName
, rSourceURL
);
2175 sal_Bool SAL_CALL
SfxDocTplService::removeTemplate( const OUString
& rGroupName
,
2176 const OUString
& rTemplateName
)
2178 return pImp
->init() && pImp
->removeTemplate( rGroupName
, rTemplateName
);
2182 sal_Bool SAL_CALL
SfxDocTplService::renameTemplate( const OUString
& rGroupName
,
2183 const OUString
& rOldName
,
2184 const OUString
& rNewName
)
2186 if ( rOldName
== rNewName
)
2189 return pImp
->init() && pImp
->renameTemplate( rGroupName
, rOldName
, rNewName
);
2193 sal_Bool SAL_CALL
SfxDocTplService::addGroup( const OUString
& rGroupName
)
2195 return pImp
->init() && pImp
->addGroup( rGroupName
);
2199 sal_Bool SAL_CALL
SfxDocTplService::removeGroup( const OUString
& rGroupName
)
2201 return pImp
->init() && pImp
->removeGroup( rGroupName
);
2205 sal_Bool SAL_CALL
SfxDocTplService::renameGroup( const OUString
& rOldName
,
2206 const OUString
& rNewName
)
2208 if ( rOldName
== rNewName
)
2211 return pImp
->init() && pImp
->renameGroup( rOldName
, rNewName
);
2215 void SAL_CALL
SfxDocTplService::update()
2221 WaitWindow_Impl::WaitWindow_Impl() : WorkWindow(nullptr, WB_BORDER
| WB_3DLOOK
)
2223 tools::Rectangle
aRect(0, 0, 300, 30000);
2224 maText
= SfxResId(RID_CNT_STR_WAITING
);
2225 maRect
= GetTextRect(aRect
, maText
, gnTextStyle
);
2227 aRect
.AdjustRight(2 * X_OFFSET
);
2228 aRect
.AdjustBottom(2 * Y_OFFSET
);
2229 maRect
.SetPos(Point(X_OFFSET
, Y_OFFSET
));
2230 SetOutputSizePixel(aRect
.GetSize());
2234 GetOutDev()->Flush();
2238 WaitWindow_Impl::~WaitWindow_Impl()
2243 void WaitWindow_Impl::dispose()
2246 WorkWindow::dispose();
2250 void WaitWindow_Impl::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*rRect*/)
2252 rRenderContext
.DrawText(maRect
, maText
, gnTextStyle
);
2255 void SfxDocTplService_Impl::addHierGroup( GroupList_Impl
& rList
,
2256 const OUString
& rTitle
,
2257 const OUString
& rOwnURL
)
2259 // now get the content of the Group
2261 uno::Reference
<XResultSet
> xResultSet
;
2265 aContent
= Content(rOwnURL
, maCmdEnv
, comphelper::getProcessComponentContext());
2266 xResultSet
= aContent
.createCursor( { TITLE
, TARGET_URL
, PROPERTY_TYPE
}, INCLUDE_DOCUMENTS_ONLY
);
2268 catch (ContentCreationException
&)
2270 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
2272 catch (Exception
&) {}
2274 if ( !xResultSet
.is() )
2277 GroupData_Impl
*pGroup
= new GroupData_Impl( rTitle
);
2278 pGroup
->setHierarchy( true );
2279 pGroup
->setHierarchyURL( rOwnURL
);
2280 rList
.push_back( std::unique_ptr
<GroupData_Impl
>(pGroup
) );
2282 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2283 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2287 while ( xResultSet
->next() )
2289 bool bUpdateType
= false;
2290 DocTemplates_EntryData_Impl
*pData
;
2292 const OUString
aTitle( xRow
->getString( 1 ) );
2293 const OUString
aTargetDir( xRow
->getString( 2 ) );
2294 OUString
aType( xRow
->getString( 3 ) );
2295 const OUString aHierURL
{xContentAccess
->queryContentIdentifierString()};
2297 if ( aType
.isEmpty() )
2301 bool bDocHasTitle
= false;
2302 getTitleFromURL( aTargetDir
, aTmpTitle
, aType
, bDocHasTitle
);
2304 if ( !aType
.isEmpty() )
2308 pData
= pGroup
->addEntry( aTitle
, aTargetDir
, aType
, aHierURL
);
2309 pData
->setUpdateType( bUpdateType
);
2312 catch ( Exception
& ) {}
2316 void SfxDocTplService_Impl::addFsysGroup( GroupList_Impl
& rList
,
2317 const OUString
& rTitle
,
2318 const OUString
& rUITitle
,
2319 const OUString
& rOwnURL
,
2320 bool bWriteableGroup
)
2324 if ( rUITitle
.isEmpty() )
2326 // reserved FS names that should not be used
2327 if ( rTitle
== "wizard" )
2329 else if ( rTitle
== "internal" )
2332 aTitle
= getLongName( rTitle
);
2337 if ( aTitle
.isEmpty() )
2340 GroupData_Impl
* pGroup
= nullptr;
2341 for (const std::unique_ptr
<GroupData_Impl
>& i
: rList
)
2343 if ( i
->getTitle() == aTitle
)
2352 pGroup
= new GroupData_Impl( aTitle
);
2353 rList
.push_back( std::unique_ptr
<GroupData_Impl
>(pGroup
) );
2356 if ( bWriteableGroup
)
2357 pGroup
->setTargetURL( rOwnURL
);
2361 // now get the content of the Group
2363 uno::Reference
< XResultSet
> xResultSet
;
2364 Sequence
< OUString
> aProps
{ TITLE
};
2368 // this method is only used during checking of the available template-folders
2369 // that should happen quietly
2370 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
2371 aContent
= Content( rOwnURL
, aQuietEnv
, comphelper::getProcessComponentContext() );
2372 xResultSet
= aContent
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
2374 catch ( Exception
& ) {}
2376 if ( !xResultSet
.is() )
2379 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2380 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2384 while ( xResultSet
->next() )
2386 OUString
aChildTitle( xRow
->getString( 1 ) );
2387 const OUString aTargetURL
{xContentAccess
->queryContentIdentifierString()};
2390 if ( aChildTitle
== "sfx.tlx" || aChildTitle
== "groupuinames.xml" )
2393 bool bDocHasTitle
= false;
2394 getTitleFromURL( aTargetURL
, aChildTitle
, aType
, bDocHasTitle
);
2396 pGroup
->addEntry( aChildTitle
, aTargetURL
, aType
, OUString() );
2399 catch ( Exception
& ) {}
2403 void SfxDocTplService_Impl::createFromContent( GroupList_Impl
& rList
,
2406 bool bWriteableContent
)
2408 const OUString aTargetURL
{rContent
.get()->getIdentifier()->getContentIdentifier()};
2410 // when scanning the file system, we have to add the 'standard' group, too
2413 const OUString aUIStdTitle
{getLongName( STANDARD_FOLDER
)};
2414 addFsysGroup( rList
, OUString(), aUIStdTitle
, aTargetURL
, bWriteableContent
);
2417 // search for predefined UI names
2418 INetURLObject
aLayerObj( aTargetURL
);
2420 // TODO/LATER: Use hashmap in future
2421 std::vector
< beans::StringPair
> aUINames
;
2423 aUINames
= ReadUINamesForTemplateDir_Impl( aLayerObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) );
2425 uno::Reference
< XResultSet
> xResultSet
;
2426 Sequence
< OUString
> aProps
{ TITLE
};
2430 xResultSet
= rContent
.createCursor( aProps
, INCLUDE_FOLDERS_ONLY
);
2432 catch ( Exception
& ) {}
2434 if ( !xResultSet
.is() )
2437 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2438 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2442 while ( xResultSet
->next() )
2444 // TODO/LATER: clarify the encoding of the Title
2445 const OUString
aTitle( xRow
->getString( 1 ) );
2446 const OUString
aTargetSubfolderURL( xContentAccess
->queryContentIdentifierString() );
2449 addHierGroup( rList
, aTitle
, aTargetSubfolderURL
);
2453 for (const beans::StringPair
& rUIName
: aUINames
)
2454 if ( rUIName
.First
== aTitle
)
2456 aUITitle
= rUIName
.Second
;
2460 addFsysGroup( rList
, aTitle
, aUITitle
, aTargetSubfolderURL
, bWriteableContent
);
2464 catch ( Exception
& ) {}
2468 void SfxDocTplService_Impl::removeFromHierarchy( DocTemplates_EntryData_Impl
const *pData
)
2472 if ( Content::create( pData
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2474 removeContent( aTemplate
);
2479 void SfxDocTplService_Impl::addToHierarchy( GroupData_Impl
const *pGroup
,
2480 DocTemplates_EntryData_Impl
const *pData
)
2482 Content aGroup
, aTemplate
;
2484 if ( ! Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2487 // Check, if there's a template with the given name in this group
2488 // Return if there is already a template
2489 INetURLObject
aGroupObj( pGroup
->getHierarchyURL() );
2491 aGroupObj
.insertName( pData
->getTitle(), false,
2492 INetURLObject::LAST_SEGMENT
,
2493 INetURLObject::EncodeMechanism::All
);
2495 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2497 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2500 addEntry( aGroup
, pData
->getTitle(),
2501 pData
->getTargetURL(),
2506 void SfxDocTplService_Impl::updateData( DocTemplates_EntryData_Impl
const *pData
)
2510 if ( ! Content::create( pData
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2513 if ( pData
->getUpdateType() )
2515 setProperty( aTemplate
, PROPERTY_TYPE
, makeAny( pData
->getType() ) );
2518 if ( pData
->getUpdateLink() )
2520 setProperty( aTemplate
, TARGET_URL
, makeAny( pData
->getTargetURL() ) );
2525 void SfxDocTplService_Impl::addGroupToHierarchy( GroupData_Impl
*pGroup
)
2529 INetURLObject
aNewGroupObj( maRootURL
);
2530 aNewGroupObj
.insertName( pGroup
->getTitle(), false,
2531 INetURLObject::LAST_SEGMENT
,
2532 INetURLObject::EncodeMechanism::All
);
2534 const OUString aNewGroupURL
{aNewGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2536 if ( createFolder( aNewGroupURL
, false, false, aGroup
) )
2538 setProperty( aGroup
, TARGET_DIR_URL
, makeAny( pGroup
->getTargetURL() ) );
2539 pGroup
->setHierarchyURL( aNewGroupURL
);
2541 size_t nCount
= pGroup
->count();
2542 for ( size_t i
= 0; i
< nCount
; i
++ )
2544 DocTemplates_EntryData_Impl
*pData
= pGroup
->getEntry( i
);
2545 addToHierarchy( pGroup
, pData
); // add entry to hierarchy
2551 void SfxDocTplService_Impl::removeFromHierarchy( GroupData_Impl
const *pGroup
)
2555 if ( Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2557 removeContent( aGroup
);
2562 GroupData_Impl::GroupData_Impl( const OUString
& rTitle
)
2563 : maTitle(rTitle
), mbInUse(false), mbInHierarchy(false)
2568 DocTemplates_EntryData_Impl
* GroupData_Impl::addEntry( const OUString
& rTitle
,
2569 const OUString
& rTargetURL
,
2570 const OUString
& rType
,
2571 const OUString
& rHierURL
)
2573 DocTemplates_EntryData_Impl
* pData
= nullptr;
2574 bool EntryFound
= false;
2576 for (auto const & p
: maEntries
)
2579 if ( pData
->getTitle() == rTitle
)
2588 pData
= new DocTemplates_EntryData_Impl( rTitle
);
2589 pData
->setTargetURL( rTargetURL
);
2590 pData
->setType( rType
);
2591 if ( !rHierURL
.isEmpty() )
2593 pData
->setHierarchyURL( rHierURL
);
2594 pData
->setHierarchy( true );
2596 maEntries
.emplace_back( pData
);
2600 if ( !rHierURL
.isEmpty() )
2602 pData
->setHierarchyURL( rHierURL
);
2603 pData
->setHierarchy( true );
2606 if ( pData
->getInHierarchy() )
2609 if ( rTargetURL
!= pData
->getTargetURL() )
2611 pData
->setTargetURL( rTargetURL
);
2612 pData
->setUpdateLink( true );
2620 DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( const OUString
& rTitle
)
2621 : maTitle(rTitle
), mbInHierarchy(false), mbInUse(false), mbUpdateType(false), mbUpdateLink(false)
2628 bool SfxURLRelocator_Impl::propertyCanContainOfficeDir(
2629 std::u16string_view rPropName
)
2631 // Note: TargetURL is handled by UCB itself (because it is a property
2632 // with a predefined semantic). Additional Core properties introduced
2633 // be a client app must be handled by the client app itself, because
2634 // the UCB does not know the semantics of those properties.
2635 return ( rPropName
== TARGET_DIR_URL
|| rPropName
== PROPERTY_DIRLIST
);
2639 SfxURLRelocator_Impl::SfxURLRelocator_Impl( const uno::Reference
< XComponentContext
> & xContext
)
2640 : mxContext( xContext
)
2645 SfxURLRelocator_Impl::~SfxURLRelocator_Impl()
2650 void SfxURLRelocator_Impl::initOfficeInstDirs()
2652 if ( !mxOfficeInstDirs
.is() )
2654 std::scoped_lock
aGuard( maMutex
);
2655 if ( !mxOfficeInstDirs
.is() )
2657 OSL_ENSURE( mxContext
.is(), "No service manager!" );
2659 mxOfficeInstDirs
= theOfficeInstallationDirectories::get(mxContext
);
2665 void SfxURLRelocator_Impl::implExpandURL( OUString
& io_url
)
2667 const INetURLObject
aParser( io_url
);
2668 if ( aParser
.GetProtocol() != INetProtocol::VndSunStarExpand
)
2671 io_url
= aParser
.GetURLPath( INetURLObject::DecodeMechanism::WithCharset
);
2674 if ( !mxMacroExpander
.is() )
2676 mxMacroExpander
.set( theMacroExpander::get(mxContext
), UNO_SET_THROW
);
2678 io_url
= mxMacroExpander
->expandMacros( io_url
);
2680 catch( const Exception
& )
2682 DBG_UNHANDLED_EXCEPTION("sfx.doc");
2687 void SfxURLRelocator_Impl::makeRelocatableURL( OUString
& rURL
)
2689 if ( !rURL
.isEmpty() )
2691 initOfficeInstDirs();
2692 implExpandURL( rURL
);
2693 rURL
= mxOfficeInstDirs
->makeRelocatableURL( rURL
);
2698 void SfxURLRelocator_Impl::makeAbsoluteURL( OUString
& rURL
)
2700 if ( !rURL
.isEmpty() )
2702 initOfficeInstDirs();
2703 implExpandURL( rURL
);
2704 rURL
= mxOfficeInstDirs
->makeAbsoluteURL( rURL
);
2708 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
2709 com_sun_star_comp_sfx2_DocumentTemplates_get_implementation(
2710 css::uno::XComponentContext
*context
,
2711 css::uno::Sequence
<css::uno::Any
> const &)
2713 return cppu::acquire(new SfxDocTplService(context
));
2716 OUString
DocTemplLocaleHelper::GetStandardGroupString()
2718 return SfxResId(TEMPLATE_LONG_NAMES_ARY
[0]);
2721 std::vector
<OUString
> DocTemplLocaleHelper::GetBuiltInGroupNames()
2723 std::vector
<OUString
> aGroups
;
2724 for(auto const & aGroupName
: TEMPLATE_LONG_NAMES_ARY
)
2725 aGroups
.push_back(SfxResId(aGroupName
));
2729 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */