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 <comphelper/diagnose_ex.hxx>
22 #include <tools/urlobj.hxx>
23 #include <rtl/uri.hxx>
24 #include <rtl/ustring.hxx>
25 #include <sal/log.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/wrkwin.hxx>
29 #include <unotools/pathoptions.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/propertysequence.hxx>
32 #include <comphelper/propertyvalue.hxx>
33 #include <comphelper/sequenceashashmap.hxx>
34 #include <comphelper/storagehelper.hxx>
35 #include <comphelper/string.hxx>
36 #include <cppuhelper/implbase.hxx>
37 #include <cppuhelper/supportsservice.hxx>
38 #include <com/sun/star/beans/IllegalTypeException.hpp>
39 #include <com/sun/star/beans/PropertyAttribute.hpp>
40 #include <com/sun/star/beans/PropertyExistException.hpp>
41 #include <com/sun/star/beans/XPropertySetInfo.hpp>
42 #include <com/sun/star/beans/XPropertyContainer.hpp>
43 #include <com/sun/star/beans/StringPair.hpp>
44 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
45 #include <com/sun/star/util/theMacroExpander.hpp>
46 #include <com/sun/star/util/theOfficeInstallationDirectories.hpp>
47 #include <com/sun/star/configuration/theDefaultProvider.hpp>
48 #include <com/sun/star/document/XTypeDetection.hpp>
49 #include <com/sun/star/document/DocumentProperties.hpp>
50 #include <com/sun/star/io/TempFile.hpp>
51 #include <com/sun/star/sdbc/XResultSet.hpp>
52 #include <com/sun/star/sdbc/XRow.hpp>
53 #include <com/sun/star/ucb/ContentCreationException.hpp>
54 #include <com/sun/star/ucb/NameClash.hpp>
55 #include <com/sun/star/ucb/NameClashException.hpp>
56 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
57 #include <com/sun/star/ucb/XContentAccess.hpp>
58 #include <com/sun/star/frame/ModuleManager.hpp>
59 #include <com/sun/star/uno/Exception.hpp>
60 #include <com/sun/star/task/InteractionHandler.hpp>
61 #include <com/sun/star/ucb/XProgressHandler.hpp>
62 #include <com/sun/star/container/XNameAccess.hpp>
63 #include <com/sun/star/frame/XDocumentTemplates.hpp>
64 #include <com/sun/star/frame/XStorable.hpp>
65 #include <com/sun/star/lang/Locale.hpp>
66 #include <com/sun/star/lang/XLocalizable.hpp>
67 #include <com/sun/star/lang/XServiceInfo.hpp>
68 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
69 #include <com/sun/star/ucb/XContent.hpp>
70 #include <com/sun/star/beans/PropertyValue.hpp>
71 #include <com/sun/star/uno/RuntimeException.hpp>
72 #include <com/sun/star/uno/XComponentContext.hpp>
73 #include <com/sun/star/util/thePathSettings.hpp>
75 #include <svtools/templatefoldercache.hxx>
76 #include <unotools/configmgr.hxx>
77 #include <unotools/ucbhelper.hxx>
78 #include <i18nlangtag/languagetag.hxx>
79 #include <ucbhelper/content.hxx>
80 #include <o3tl/string_view.hxx>
82 #include <sfx2/sfxresid.hxx>
83 #include <sfxurlrelocator.hxx>
84 #include "doctemplateslocal.hxx"
85 #include <sfx2/docfac.hxx>
86 #include <sfx2/strings.hrc>
87 #include <doctempl.hrc>
92 constexpr OUStringLiteral SERVICENAME_TYPEDETECTION
= u
"com.sun.star.document.TypeDetection";
94 constexpr OUStringLiteral TEMPLATE_ROOT_URL
= u
"vnd.sun.star.hier:/templates";
95 constexpr OUString TITLE
= u
"Title"_ustr
;
96 constexpr OUString IS_FOLDER
= u
"IsFolder"_ustr
;
97 constexpr OUString IS_DOCUMENT
= u
"IsDocument"_ustr
;
98 constexpr OUString TARGET_URL
= u
"TargetURL"_ustr
;
99 constexpr OUStringLiteral TEMPLATE_VERSION
= u
"TemplateComponentVersion";
100 constexpr OUStringLiteral TEMPLATE_VERSION_VALUE
= u
"2";
101 constexpr OUStringLiteral TYPE_FOLDER
= u
"application/vnd.sun.star.hier-folder";
102 constexpr OUStringLiteral TYPE_LINK
= u
"application/vnd.sun.star.hier-link";
103 constexpr OUString TYPE_FSYS_FOLDER
= u
"application/vnd.sun.staroffice.fsys-folder"_ustr
;
104 constexpr OUStringLiteral TYPE_FSYS_FILE
= u
"application/vnd.sun.staroffice.fsys-file";
106 constexpr OUString PROPERTY_DIRLIST
= u
"DirectoryList"_ustr
;
107 constexpr OUString PROPERTY_NEEDSUPDATE
= u
"NeedsUpdate"_ustr
;
108 constexpr OUString PROPERTY_TYPE
= u
"TypeDescription"_ustr
;
110 constexpr OUString TARGET_DIR_URL
= u
"TargetDirURL"_ustr
;
111 constexpr OUStringLiteral COMMAND_DELETE
= u
"delete";
113 constexpr OUString STANDARD_FOLDER
= u
"standard"_ustr
;
117 using namespace ::com::sun::star
;
118 using namespace ::com::sun::star::beans
;
119 using namespace ::com::sun::star::document
;
120 using namespace ::com::sun::star::io
;
121 using namespace ::com::sun::star::lang
;
122 using namespace ::com::sun::star::sdbc
;
123 using namespace ::com::sun::star::ucb
;
124 using namespace ::com::sun::star::uno
;
125 using namespace ::com::sun::star::container
;
126 using namespace ::com::sun::star::util
;
128 using namespace ::ucbhelper
;
129 using namespace ::comphelper
;
135 class WaitWindow_Impl
: public WorkWindow
137 tools::Rectangle maRect
;
139 static constexpr DrawTextFlags gnTextStyle
= DrawTextFlags::Center
| DrawTextFlags::VCenter
| DrawTextFlags::WordBreak
| DrawTextFlags::MultiLine
;
143 virtual ~WaitWindow_Impl() override
;
144 virtual void dispose() override
;
145 virtual void Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
) override
;
154 OUString maShortName
;
158 class DocTemplates_EntryData_Impl
;
159 class GroupData_Impl
;
161 typedef vector
< std::unique_ptr
<GroupData_Impl
> > GroupList_Impl
;
164 class TplTaskEnvironment
: public ::cppu::WeakImplHelper
< ucb::XCommandEnvironment
>
166 uno::Reference
< task::XInteractionHandler
> m_xInteractionHandler
;
169 explicit TplTaskEnvironment( uno::Reference
< task::XInteractionHandler
> xInteractionHandler
)
170 : m_xInteractionHandler(std::move( xInteractionHandler
))
173 virtual uno::Reference
<task::XInteractionHandler
> SAL_CALL
getInteractionHandler() override
174 { return m_xInteractionHandler
; }
176 virtual uno::Reference
<ucb::XProgressHandler
> SAL_CALL
getProgressHandler() override
177 { return uno::Reference
<ucb::XProgressHandler
>(); }
180 class SfxDocTplService
: public ::cppu::WeakImplHelper
< css::lang::XLocalizable
, css::frame::XDocumentTemplates
, css::lang::XServiceInfo
>
183 explicit SfxDocTplService( const css::uno::Reference
< uno::XComponentContext
>& xContext
);
184 virtual ~SfxDocTplService() override
;
186 virtual OUString SAL_CALL
getImplementationName() override
188 return "com.sun.star.comp.sfx2.DocumentTemplates";
191 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
) override
193 return cppu::supportsService(this, ServiceName
);
196 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
198 css::uno::Sequence
< OUString
> aSeq
{ "com.sun.star.frame.DocumentTemplates" };
203 // --- XLocalizable ---
204 void SAL_CALL
setLocale( const css::lang::Locale
& eLocale
) override
;
205 css::lang::Locale SAL_CALL
getLocale() override
;
207 // --- XDocumentTemplates ---
208 css::uno::Reference
< css::ucb::XContent
> SAL_CALL
getContent() override
;
209 sal_Bool SAL_CALL
storeTemplate( const OUString
& GroupName
,
210 const OUString
& TemplateName
,
211 const css::uno::Reference
< css::frame::XStorable
>& Storable
) override
;
212 sal_Bool SAL_CALL
addTemplate( const OUString
& GroupName
,
213 const OUString
& TemplateName
,
214 const OUString
& SourceURL
) override
;
215 sal_Bool SAL_CALL
removeTemplate( const OUString
& GroupName
,
216 const OUString
& TemplateName
) override
;
217 sal_Bool SAL_CALL
renameTemplate( const OUString
& GroupName
,
218 const OUString
& OldTemplateName
,
219 const OUString
& NewTemplateName
) override
;
220 sal_Bool SAL_CALL
addGroup( const OUString
& GroupName
) override
;
221 sal_Bool SAL_CALL
removeGroup( const OUString
& GroupName
) override
;
222 sal_Bool SAL_CALL
renameGroup( const OUString
& OldGroupName
,
223 const OUString
& NewGroupName
) override
;
224 void SAL_CALL
update() override
;
227 bool init() { if ( !mbIsInitialized
) init_Impl(); return mbIsInitialized
; }
231 uno::Reference
< XComponentContext
> mxContext
;
232 uno::Reference
< XCommandEnvironment
> maCmdEnv
;
233 uno::Reference
< XDocumentProperties
> m_xDocProps
;
234 uno::Reference
< XTypeDetection
> mxType
;
236 ::osl::Mutex maMutex
;
237 Sequence
< OUString
> maTemplateDirs
;
238 Sequence
< OUString
> maInternalTemplateDirs
;
240 std::vector
< NamePair_Impl
> maNames
;
241 lang::Locale maLocale
;
242 Content maRootContent
;
243 bool mbIsInitialized
: 1;
244 bool mbLocaleSet
: 1;
246 SfxURLRelocator_Impl maRelocator
;
249 void getDefaultLocale();
251 void readFolderList();
253 OUString
getLongName( const OUString
& rShortName
);
254 bool setTitleForURL( const OUString
& rURL
, const OUString
& aTitle
);
255 void getTitleFromURL( const OUString
& rURL
, OUString
& aTitle
, OUString
& aType
, bool& bDocHasTitle
);
257 bool addEntry( Content
& rParentFolder
,
258 const OUString
& rTitle
,
259 const OUString
& rTargetURL
,
260 const OUString
& rType
);
262 bool createFolder( const OUString
& rNewFolderURL
,
265 Content
&rNewFolder
);
267 static bool CreateNewUniqueFolderWithPrefix( std::u16string_view aPath
,
268 const OUString
& aPrefix
,
269 OUString
& aNewFolderName
,
270 OUString
& aNewFolderURL
,
271 Content
& aNewFolder
);
272 static OUString
CreateNewUniqueFileWithPrefix( std::u16string_view aPath
,
273 const OUString
& aPrefix
,
274 std::u16string_view aExt
);
276 std::vector
< beans::StringPair
> ReadUINamesForTemplateDir_Impl( std::u16string_view aUserPath
);
277 bool UpdateUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
278 const OUString
& aGroupName
,
279 const OUString
& aNewFolderName
);
280 bool ReplaceUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
281 const OUString
& aFsysGroupName
,
282 std::u16string_view aOldGroupName
,
283 const OUString
& aNewGroupName
);
284 void RemoveUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
285 std::u16string_view aGroupName
);
286 bool WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
287 const std::vector
< beans::StringPair
>& aUINames
);
289 OUString
CreateNewGroupFsys( const OUString
& rGroupName
, Content
& aGroup
);
291 static bool removeContent( Content
& rContent
);
292 bool removeContent( const OUString
& rContentURL
);
294 bool setProperty( Content
& rContent
,
295 const OUString
& rPropName
,
296 const Any
& rPropValue
);
297 bool getProperty( Content
& rContent
,
298 const OUString
& rPropName
,
301 void createFromContent( GroupList_Impl
& rList
,
304 bool bWriteableContent
);
305 void addHierGroup( GroupList_Impl
& rList
,
306 const OUString
& rTitle
,
307 const OUString
& rOwnURL
);
308 void addFsysGroup( GroupList_Impl
& rList
,
309 const OUString
& rTitle
,
310 const OUString
& rUITitle
,
311 const OUString
& rOwnURL
,
312 bool bWriteableGroup
);
313 void removeFromHierarchy( DocTemplates_EntryData_Impl
const *pData
);
314 void addToHierarchy( GroupData_Impl
const *pGroup
,
315 DocTemplates_EntryData_Impl
const *pData
);
317 void removeFromHierarchy( GroupData_Impl
const *pGroup
);
318 void addGroupToHierarchy( GroupData_Impl
*pGroup
);
320 void updateData( DocTemplates_EntryData_Impl
const *pData
);
322 //See: #i66157# and rhbz#1065807
323 //return which template dir the rURL is a subpath of
324 OUString
findParentTemplateDir(const OUString
& rURL
) const;
326 //See: #i66157# and rhbz#1065807
327 //return true if rURL is a path (or subpath of) a dir which is not a user path
328 //which implies neither it or its contents can be removed
329 bool isInternalTemplateDir(const OUString
& rURL
) const;
333 class DocTemplates_EntryData_Impl
337 OUString maTargetURL
;
338 OUString maHierarchyURL
;
340 bool mbInHierarchy
: 1;
342 bool mbUpdateType
: 1;
343 bool mbUpdateLink
: 1;
346 explicit DocTemplates_EntryData_Impl( OUString aTitle
);
348 void setInUse() { mbInUse
= true; }
349 void setHierarchy( bool bInHierarchy
) { mbInHierarchy
= bInHierarchy
; }
350 void setUpdateLink( bool bUpdateLink
) { mbUpdateLink
= bUpdateLink
; }
351 void setUpdateType( bool bUpdateType
) { mbUpdateType
= bUpdateType
; }
353 bool getInUse() const { return mbInUse
; }
354 bool getInHierarchy() const { return mbInHierarchy
; }
355 bool getUpdateLink() const { return mbUpdateLink
; }
356 bool getUpdateType() const { return mbUpdateType
; }
358 const OUString
& getHierarchyURL() const { return maHierarchyURL
; }
359 const OUString
& getTargetURL() const { return maTargetURL
; }
360 const OUString
& getTitle() const { return maTitle
; }
361 const OUString
& getType() const { return maType
; }
363 void setHierarchyURL( const OUString
& rURL
) { maHierarchyURL
= rURL
; }
364 void setTargetURL( const OUString
& rURL
) { maTargetURL
= rURL
; }
365 void setType( const OUString
& rType
) { maType
= rType
; }
371 std::vector
< std::unique_ptr
<DocTemplates_EntryData_Impl
> > maEntries
;
373 OUString maHierarchyURL
;
374 OUString maTargetURL
;
376 bool mbInHierarchy
: 1;
379 explicit GroupData_Impl( OUString aTitle
);
381 void setInUse() { mbInUse
= true; }
382 void setHierarchy( bool bInHierarchy
) { mbInHierarchy
= bInHierarchy
; }
383 void setHierarchyURL( const OUString
& rURL
) { maHierarchyURL
= rURL
; }
384 void setTargetURL( const OUString
& rURL
) { maTargetURL
= rURL
; }
386 bool getInUse() const { return mbInUse
; }
387 bool getInHierarchy() const { return mbInHierarchy
; }
388 const OUString
& getHierarchyURL() const { return maHierarchyURL
; }
389 const OUString
& getTargetURL() const { return maTargetURL
; }
390 const OUString
& getTitle() const { return maTitle
; }
392 DocTemplates_EntryData_Impl
* addEntry( const OUString
& rTitle
,
393 const OUString
& rTargetURL
,
394 const OUString
& rType
,
395 const OUString
& rHierURL
);
396 size_t count() { return maEntries
.size(); }
397 DocTemplates_EntryData_Impl
* getEntry( size_t nPos
) { return maEntries
[ nPos
].get(); }
401 // private SfxDocTplService_Impl
403 void SfxDocTplService::init_Impl()
405 uno::Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
406 uno::Reference
< task::XInteractionHandler
> xInteractionHandler(
407 task::InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
408 maCmdEnv
= new TplTaskEnvironment( xInteractionHandler
);
410 ::osl::ClearableMutexGuard
aGuard( maMutex
);
411 bool bIsInitialized
= false;
412 bool bNeedsUpdate
= false;
417 // convert locale to string
418 // set maRootContent to the root of the templates hierarchy. Create the
419 // entry if necessary
421 maRootURL
= TEMPLATE_ROOT_URL
+ "/" + LanguageTag::convertToBcp47(maLocale
);
423 const OUString
aTemplVersPropName( TEMPLATE_VERSION
);
424 const OUString
aTemplVers( TEMPLATE_VERSION_VALUE
);
425 if ( Content::create( maRootURL
, maCmdEnv
, comphelper::getProcessComponentContext(), maRootContent
) )
429 if ( getProperty( maRootContent
, aTemplVersPropName
, aValue
)
430 && ( aValue
>>= aPropValue
)
431 && aPropValue
== aTemplVers
)
433 bIsInitialized
= true;
436 removeContent( maRootContent
);
439 if ( !bIsInitialized
)
441 if ( createFolder( maRootURL
, true, false, maRootContent
)
442 && setProperty( maRootContent
, aTemplVersPropName
, uno::Any( aTemplVers
) ) )
443 bIsInitialized
= true;
448 if ( bIsInitialized
)
451 m_xDocProps
.set(document::DocumentProperties::create(
452 ::comphelper::getProcessComponentContext()));
453 } catch (uno::RuntimeException
const&) {
454 TOOLS_WARN_EXCEPTION("sfx.doc", "SfxDocTplService_Impl::init_Impl: cannot create DocumentProperties service:");
457 mxType
.set( mxContext
->getServiceManager()->createInstanceWithContext(SERVICENAME_TYPEDETECTION
, mxContext
), UNO_QUERY
);
465 SolarMutexClearableGuard aSolarGuard
;
467 VclPtrInstance
< WaitWindow_Impl
> pWin
;
470 osl::MutexGuard
anotherGuard(maMutex
);
473 SolarMutexGuard aSecondSolarGuard
;
475 pWin
.disposeAndClear();
477 else if ( needsUpdate() )
478 // the UI should be shown only on the first update
483 SAL_WARN( "sfx.doc", "init_Impl(): Could not create root" );
486 mbIsInitialized
= bIsInitialized
;
490 void SfxDocTplService::getDefaultLocale()
494 ::osl::MutexGuard
aGuard( maMutex
);
497 maLocale
= LanguageTag::convertToLocale( utl::ConfigManager::getUILocale(), false);
503 const char* TEMPLATE_SHORT_NAMES_ARY
[] =
515 void SfxDocTplService::readFolderList()
517 SolarMutexGuard aGuard
;
519 static_assert( SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY
) == SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY
), "mismatch array lengths" );
520 const size_t nCount
= std::min(SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY
), SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY
));
521 for (size_t i
= 0; i
< nCount
; ++i
)
524 aPair
.maShortName
= OUString::createFromAscii(TEMPLATE_SHORT_NAMES_ARY
[i
]);
525 aPair
.maLongName
= SfxResId(TEMPLATE_LONG_NAMES_ARY
[i
]);
527 maNames
.push_back( aPair
);
532 OUString
SfxDocTplService::getLongName( const OUString
& rShortName
)
536 for (auto const & rPair
: maNames
)
538 if ( rPair
.maShortName
== rShortName
)
540 aRet
= rPair
.maLongName
;
545 if ( aRet
.isEmpty() )
552 void SfxDocTplService::getDirList()
556 // Get the template dir list
557 // TODO/LATER: let use service, register listener
559 OUString aDirs
= SvtPathOptions().GetTemplatePath();
560 sal_Int32 nCount
= comphelper::string::getTokenCount(aDirs
, C_DELIM
);
562 maTemplateDirs
= Sequence
< OUString
>( nCount
);
564 uno::Reference
< util::XMacroExpander
> xExpander
= util::theMacroExpander::get(mxContext
);
567 for (auto& rTemplateDir
: asNonConstRange(maTemplateDirs
))
569 aURL
.SetSmartProtocol( INetProtocol::File
);
570 aURL
.SetURL( o3tl::getToken(aDirs
, 0, C_DELIM
, nIdx
) );
571 rTemplateDir
= aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
573 if (xExpander
&& rTemplateDir
.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &rTemplateDir
))
576 = rtl::Uri::decode(rTemplateDir
, rtl_UriDecodeStrict
, RTL_TEXTENCODING_UTF8
);
577 rTemplateDir
= xExpander
->expandMacros( rTemplateDir
);
581 aValue
<<= maTemplateDirs
;
583 css::uno::Reference
< css::util::XPathSettings
> xPathSettings
=
584 css::util::thePathSettings::get(mxContext
);
586 // load internal paths
587 Any aAny
= xPathSettings
->getPropertyValue( "Template_internal" );
588 aAny
>>= maInternalTemplateDirs
;
590 for (auto& rInternalTemplateDir
: asNonConstRange(maInternalTemplateDirs
))
592 //expand vnd.sun.star.expand: and remove "..." from them
593 //to normalize into the expected url patterns
594 maRelocator
.makeRelocatableURL(rInternalTemplateDir
);
595 maRelocator
.makeAbsoluteURL(rInternalTemplateDir
);
598 // Store the template dir list
599 setProperty( maRootContent
, PROPERTY_DIRLIST
, aValue
);
603 bool SfxDocTplService::needsUpdate()
605 bool bNeedsUpdate
= true;
608 // Get the template dir list
609 bool bHasProperty
= getProperty( maRootContent
, PROPERTY_NEEDSUPDATE
, aValue
);
612 aValue
>>= bNeedsUpdate
;
614 // the old template component also checks this state, but it is initialized from this component
615 // so if this component was already updated the old component does not need such an update
616 ::svt::TemplateFolderCache aTempCache
;
618 bNeedsUpdate
= aTempCache
.needsUpdate();
621 aTempCache
.storeState();
627 bool SfxDocTplService::setTitleForURL( const OUString
& rURL
, const OUString
& aTitle
)
629 if (m_xDocProps
.is())
633 m_xDocProps
->loadFromMedium(rURL
, Sequence
<PropertyValue
>());
634 m_xDocProps
->setTitle(aTitle
);
636 uno::Reference
< embed::XStorage
> xStorage
= ::comphelper::OStorageHelper::GetStorageFromURL(
637 rURL
, embed::ElementModes::READWRITE
);
639 uno::Sequence
<beans::PropertyValue
> medium( comphelper::InitPropertySequence({
640 { "DocumentBaseURL", Any(rURL
) },
644 m_xDocProps
->storeToStorage(xStorage
, medium
);
655 void SfxDocTplService::getTitleFromURL( const OUString
& rURL
, OUString
& aTitle
, OUString
& aType
, bool& bDocHasTitle
)
657 bDocHasTitle
= false;
659 if (m_xDocProps
.is())
663 m_xDocProps
->loadFromMedium(rURL
, Sequence
<PropertyValue
>());
664 aTitle
= m_xDocProps
->getTitle();
671 if ( aType
.isEmpty() && mxType
.is() )
673 const OUString aDocType
{mxType
->queryTypeByURL( rURL
)};
674 if ( !aDocType
.isEmpty() )
677 uno::Reference
< container::XNameAccess
> xTypeDetection( mxType
, uno::UNO_QUERY_THROW
);
678 SequenceAsHashMap
aTypeProps( xTypeDetection
->getByName( aDocType
) );
679 aType
= aTypeProps
.getUnpackedValueOrDefault(
683 catch( uno::Exception
& )
687 if ( aTitle
.isEmpty() )
689 INetURLObject
aURL( rURL
);
691 aTitle
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true,
692 INetURLObject::DecodeMechanism::WithCharset
);
699 bool SfxDocTplService::addEntry( Content
& rParentFolder
,
700 const OUString
& rTitle
,
701 const OUString
& rTargetURL
,
702 const OUString
& rType
)
704 bool bAddedEntry
= false;
706 INetURLObject
aLinkObj( rParentFolder
.getURL() );
707 aLinkObj
.insertName( rTitle
, false,
708 INetURLObject::LAST_SEGMENT
,
709 INetURLObject::EncodeMechanism::All
);
710 const OUString aLinkURL
{aLinkObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
714 if ( ! Content::create( aLinkURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aLink
) )
716 Sequence
< Any
> aValues
{ Any(rTitle
), Any(false), Any(rTargetURL
) };
720 rParentFolder
.insertNewContent( TYPE_LINK
, { TITLE
, IS_FOLDER
, TARGET_URL
}, aValues
, aLink
);
721 setProperty( aLink
, PROPERTY_TYPE
, Any( rType
) );
731 bool SfxDocTplService::createFolder( const OUString
& rNewFolderURL
,
734 Content
&rNewFolder
)
737 bool bCreatedFolder
= false;
738 INetURLObject
aParentURL( rNewFolderURL
);
739 const OUString aFolderName
{aParentURL
.getName( INetURLObject::LAST_SEGMENT
, true,
740 INetURLObject::DecodeMechanism::WithCharset
)};
742 // compute the parent folder url from the new folder url
743 // and remove the final slash, because Content::create doesn't
745 aParentURL
.removeSegment();
746 if ( aParentURL
.getSegmentCount() >= 1 )
747 aParentURL
.removeFinalSlash();
749 // if the parent exists, we can continue with the creation of the
750 // new folder, we have to create the parent otherwise ( as long as
751 // bCreateParent is set to true )
752 if ( Content::create( aParentURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), maCmdEnv
, comphelper::getProcessComponentContext(), aParent
) )
756 Sequence
< Any
> aValues
{ Any(aFolderName
), Any(true) };
760 aType
= TYPE_FSYS_FOLDER
;
764 aParent
.insertNewContent( aType
, { TITLE
, IS_FOLDER
}, aValues
, rNewFolder
);
765 bCreatedFolder
= true;
767 catch( Exception
const & )
769 TOOLS_WARN_EXCEPTION( "sfx.doc", "createFolder(): Could not create new folder" );
772 else if ( bCreateParent
)
774 // if the parent doesn't exists and bCreateParent is set to true,
775 // we try to create the parent and if this was successful, we
776 // try to create the new folder again ( but this time, we set
777 // bCreateParent to false to avoid endless recursions )
778 if ( ( aParentURL
.getSegmentCount() >= 1 ) &&
779 createFolder( aParentURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), bCreateParent
, bFsysFolder
, aParent
) )
781 bCreatedFolder
= createFolder( rNewFolderURL
, false, bFsysFolder
, rNewFolder
);
785 return bCreatedFolder
;
789 bool SfxDocTplService::CreateNewUniqueFolderWithPrefix( std::u16string_view aPath
,
790 const OUString
& aPrefix
,
791 OUString
& aNewFolderName
,
792 OUString
& aNewFolderURL
,
793 Content
& aNewFolder
)
795 bool bCreated
= false;
796 INetURLObject
aDirPath( aPath
);
799 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
800 if ( Content::create( aDirPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), aQuietEnv
, comphelper::getProcessComponentContext(), aParent
) )
802 for ( sal_Int32 nInd
= 0; nInd
< 32000; nInd
++ )
804 OUString aTryName
= aPrefix
;
806 aTryName
+= OUString::number( nInd
);
810 Sequence
< Any
> aValues
{ Any(aTryName
), Any(true) };
811 bCreated
= aParent
.insertNewContent( TYPE_FSYS_FOLDER
, { TITLE
, IS_FOLDER
}, aValues
, aNewFolder
);
813 catch( ucb::NameClashException
& )
815 // if there is already an element, retry
819 INetURLObject
aObjPath( aDirPath
);
820 aObjPath
.insertName( aTryName
, false,
821 INetURLObject::LAST_SEGMENT
,
822 INetURLObject::EncodeMechanism::All
);
823 // if there is already an element, retry
824 // if there was another error, do not try any more
825 if ( !::utl::UCBContentHelper::Exists( aObjPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
831 aNewFolderName
= aTryName
;
832 aNewFolderURL
= aNewFolder
.get()->getIdentifier()->getContentIdentifier();
842 OUString
SfxDocTplService::CreateNewUniqueFileWithPrefix( std::u16string_view aPath
,
843 const OUString
& aPrefix
,
844 std::u16string_view aExt
)
846 OUString aNewFileURL
;
847 INetURLObject
aDirPath( aPath
);
851 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
852 if ( Content::create( aDirPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), aQuietEnv
, comphelper::getProcessComponentContext(), aParent
) )
854 for ( sal_Int32 nInd
= 0; nInd
< 32000; nInd
++ )
857 bool bCreated
= false;
858 OUString aTryName
= aPrefix
;
860 aTryName
+= OUString::number( nInd
);
861 if ( aExt
.empty() || aExt
[0] != '.' )
867 Sequence
< Any
> aValues
{ Any(aTryName
), Any(true) };
868 bCreated
= aParent
.insertNewContent( TYPE_FSYS_FILE
, { TITLE
, IS_DOCUMENT
}, aValues
, aNewFile
);
870 catch( ucb::NameClashException
& )
872 // if there is already an element, retry
876 INetURLObject
aObjPath( aPath
);
877 aObjPath
.insertName( aTryName
, false,
878 INetURLObject::LAST_SEGMENT
,
879 INetURLObject::EncodeMechanism::All
);
880 // if there is already an element, retry
881 // if there was another error, do not try any more
882 if ( !::utl::UCBContentHelper::Exists( aObjPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
888 aNewFileURL
= aNewFile
.get()->getIdentifier()->getContentIdentifier();
898 bool SfxDocTplService::removeContent( Content
& rContent
)
900 bool bRemoved
= false;
905 rContent
.executeCommand( COMMAND_DELETE
, aArg
);
908 catch ( RuntimeException
& ) {}
909 catch ( Exception
& ) {}
915 bool SfxDocTplService::removeContent( const OUString
& rContentURL
)
919 if ( Content::create( rContentURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aContent
) )
920 return removeContent( aContent
);
925 bool SfxDocTplService::setProperty( Content
& rContent
,
926 const OUString
& rPropName
,
927 const Any
& rPropValue
)
929 bool bPropertySet
= false;
931 // Store the property
934 Any
aPropValue( rPropValue
);
935 uno::Reference
< XPropertySetInfo
> aPropInfo
= rContent
.getProperties();
937 // check, whether or not the property exists, create it, when not
938 if ( !aPropInfo
.is() || !aPropInfo
->hasPropertyByName( rPropName
) )
940 uno::Reference
< XPropertyContainer
> xProperties( rContent
.get(), UNO_QUERY
);
941 if ( xProperties
.is() )
945 xProperties
->addProperty( rPropName
, PropertyAttribute::MAYBEVOID
, rPropValue
);
947 catch( PropertyExistException
& ) {}
948 catch( IllegalTypeException
& ) {
949 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
951 catch( IllegalArgumentException
& ) {
952 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
957 // To ensure a reloctable office installation, the path to the
958 // office installation directory must never be stored directly.
959 if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName
) )
962 if ( rPropValue
>>= aValue
)
964 maRelocator
.makeRelocatableURL( aValue
);
965 aPropValue
<<= aValue
;
969 Sequence
< OUString
> aValues
;
970 if ( rPropValue
>>= aValues
)
972 for ( auto& rValue
: asNonConstRange(aValues
) )
974 maRelocator
.makeRelocatableURL( rValue
);
976 aPropValue
<<= aValues
;
980 OSL_FAIL( "Unsupported property value type" );
985 // now set the property
987 rContent
.setPropertyValue( rPropName
, aPropValue
);
990 catch ( RuntimeException
& ) {}
991 catch ( Exception
& ) {}
997 bool SfxDocTplService::getProperty(Content
& rContent
, const OUString
& rPropName
, Any
& rPropValue
)
999 bool bGotProperty
= false;
1004 uno::Reference
< XPropertySetInfo
> aPropInfo
= rContent
.getProperties();
1006 // check, whether or not the property exists
1007 if ( !aPropInfo
.is() || !aPropInfo
->hasPropertyByName( rPropName
) )
1012 // now get the property
1014 rPropValue
= rContent
.getPropertyValue( rPropName
);
1016 // To ensure a reloctable office installation, the path to the
1017 // office installation directory must never be stored directly.
1018 if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName
) )
1021 if ( rPropValue
>>= aValue
)
1023 maRelocator
.makeAbsoluteURL( aValue
);
1024 rPropValue
<<= aValue
;
1028 Sequence
< OUString
> aValues
;
1029 if ( rPropValue
>>= aValues
)
1031 for ( auto& rValue
: asNonConstRange(aValues
) )
1033 maRelocator
.makeAbsoluteURL( rValue
);
1035 rPropValue
<<= aValues
;
1039 OSL_FAIL( "Unsupported property value type" );
1044 bGotProperty
= true;
1046 catch ( RuntimeException
& ) {}
1047 catch ( Exception
& ) {}
1049 return bGotProperty
;
1052 SfxDocTplService::SfxDocTplService( const uno::Reference
< XComponentContext
> & xContext
)
1053 : mxContext(xContext
), mbIsInitialized(false), mbLocaleSet(false), maRelocator(xContext
)
1058 SfxDocTplService::~SfxDocTplService()
1060 ::osl::MutexGuard
aGuard( maMutex
);
1065 lang::Locale
SfxDocTplService::getLocale()
1067 ::osl::MutexGuard
aGuard( maMutex
);
1076 void SfxDocTplService::setLocale( const lang::Locale
&rLocale
)
1078 ::osl::MutexGuard
aGuard( maMutex
);
1080 if ( mbLocaleSet
&& (
1081 ( maLocale
.Language
!= rLocale
.Language
) ||
1082 ( maLocale
.Country
!= rLocale
.Country
) ||
1083 ( maLocale
.Variant
!= rLocale
.Variant
) ) )
1084 mbIsInitialized
= false;
1091 void SfxDocTplService::update()
1100 void SfxDocTplService::doUpdate()
1102 ::osl::MutexGuard
aGuard( maMutex
);
1104 const OUString
aPropName( PROPERTY_NEEDSUPDATE
);
1108 setProperty( maRootContent
, aPropName
, aValue
);
1110 GroupList_Impl aGroupList
;
1112 // get the entries from the hierarchy
1113 createFromContent( aGroupList
, maRootContent
, true, false );
1115 // get the entries from the template directories
1116 sal_Int32 nCountDir
= maTemplateDirs
.getLength();
1117 const OUString
* pDirs
= maTemplateDirs
.getConstArray();
1118 Content aDirContent
;
1120 // the last directory in the list must be writable
1121 bool bWriteableDirectory
= true;
1123 // the target folder might not exist, for this reason no interaction handler should be used
1124 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
1129 if ( Content::create( pDirs
[ nCountDir
], aQuietEnv
, comphelper::getProcessComponentContext(), aDirContent
) )
1131 createFromContent( aGroupList
, aDirContent
, false, bWriteableDirectory
);
1134 bWriteableDirectory
= false;
1137 // now check the list
1138 for(std::unique_ptr
<GroupData_Impl
>& pGroup
: aGroupList
)
1140 if ( pGroup
->getInUse() )
1142 if ( pGroup
->getInHierarchy() )
1145 if ( Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1146 setProperty( aGroup
,
1148 Any( pGroup
->getTargetURL() ) );
1150 size_t nCount
= pGroup
->count();
1151 for ( size_t i
=0; i
<nCount
; i
++ )
1153 DocTemplates_EntryData_Impl
*pData
= pGroup
->getEntry( i
);
1154 if ( ! pData
->getInUse() )
1156 if ( pData
->getInHierarchy() )
1157 removeFromHierarchy( pData
); // delete entry in hierarchy
1159 addToHierarchy( pGroup
.get(), pData
); // add entry to hierarchy
1161 else if ( pData
->getUpdateType() ||
1162 pData
->getUpdateLink() )
1164 updateData( pData
);
1170 addGroupToHierarchy( pGroup
.get() ); // add group to hierarchy
1174 removeFromHierarchy( pGroup
.get() ); // delete group from hierarchy
1179 setProperty( maRootContent
, aPropName
, aValue
);
1183 std::vector
< beans::StringPair
> SfxDocTplService::ReadUINamesForTemplateDir_Impl( std::u16string_view aUserPath
)
1185 INetURLObject
aLocObj( aUserPath
);
1186 aLocObj
.insertName( u
"groupuinames.xml", false,
1187 INetURLObject::LAST_SEGMENT
,
1188 INetURLObject::EncodeMechanism::All
);
1189 Content aLocContent
;
1191 // TODO/LATER: Use hashmap in future
1192 std::vector
< beans::StringPair
> aUINames
;
1193 if ( Content::create( aLocObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), uno::Reference
< ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext(), aLocContent
) )
1197 uno::Reference
< io::XInputStream
> xLocStream
= aLocContent
.openStream();
1198 if ( xLocStream
.is() )
1199 aUINames
= DocTemplLocaleHelper::ReadGroupLocalizationSequence( xLocStream
, mxContext
);
1201 catch( uno::Exception
& )
1209 bool SfxDocTplService::UpdateUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
1210 const OUString
& aGroupName
,
1211 const OUString
& aNewFolderName
)
1213 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1214 sal_Int32 nLen
= aUINames
.size();
1216 // it is possible that the name is used already, but it should be checked before
1217 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1218 if ( aUINames
[nInd
].First
== aNewFolderName
)
1221 aUINames
.resize( ++nLen
);
1222 aUINames
[nLen
-1].First
= aNewFolderName
;
1223 aUINames
[nLen
-1].Second
= aGroupName
;
1225 return WriteUINamesForTemplateDir_Impl( aUserPath
, aUINames
);
1229 bool SfxDocTplService::ReplaceUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
1230 const OUString
& aDefaultFsysGroupName
,
1231 std::u16string_view aOldGroupName
,
1232 const OUString
& aNewGroupName
)
1234 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1235 sal_Int32 nLen
= aUINames
.size();
1237 bool bChanged
= false;
1238 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1239 if ( aUINames
[nInd
].Second
== aOldGroupName
)
1241 aUINames
[nInd
].Second
= aNewGroupName
;
1247 aUINames
.resize( ++nLen
);
1248 aUINames
[nLen
-1].First
= aDefaultFsysGroupName
;
1249 aUINames
[nLen
-1].Second
= aNewGroupName
;
1251 return WriteUINamesForTemplateDir_Impl( aUserPath
, aUINames
);
1255 void SfxDocTplService::RemoveUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
1256 std::u16string_view aGroupName
)
1258 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1259 sal_Int32 nLen
= aUINames
.size();
1260 std::vector
< beans::StringPair
> aNewUINames( nLen
);
1261 sal_Int32 nNewLen
= 0;
1263 bool bChanged
= false;
1264 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1265 if ( aUINames
[nInd
].Second
== aGroupName
)
1270 aNewUINames
[nNewLen
-1].First
= aUINames
[nInd
].First
;
1271 aNewUINames
[nNewLen
-1].Second
= aUINames
[nInd
].Second
;
1274 aNewUINames
.resize( nNewLen
);
1277 WriteUINamesForTemplateDir_Impl( aUserPath
, aNewUINames
);
1281 bool SfxDocTplService::WriteUINamesForTemplateDir_Impl( std::u16string_view aUserPath
,
1282 const std::vector
< beans::StringPair
>& aUINames
)
1284 bool bResult
= false;
1286 uno::Reference
< io::XTempFile
> xTempFile(
1287 io::TempFile::create(mxContext
),
1288 uno::UNO_SET_THROW
);
1290 uno::Reference
< io::XOutputStream
> xOutStream
= xTempFile
->getOutputStream();
1291 if ( !xOutStream
.is() )
1292 throw uno::RuntimeException();
1294 DocTemplLocaleHelper::WriteGroupLocalizationSequence( xOutStream
, aUINames
, mxContext
);
1296 // the SAX writer might close the stream
1297 xOutStream
->closeOutput();
1298 } catch( uno::Exception
& )
1301 Content
aTargetContent( OUString(aUserPath
), maCmdEnv
, comphelper::getProcessComponentContext() );
1302 Content
aSourceContent( xTempFile
->getUri(), maCmdEnv
, comphelper::getProcessComponentContext() );
1303 aTargetContent
.transferContent( aSourceContent
,
1304 InsertOperation::Copy
,
1306 ucb::NameClash::OVERWRITE
,
1311 catch ( uno::Exception
& )
1313 TOOLS_WARN_EXCEPTION("sfx.doc", "");
1320 OUString
SfxDocTplService::CreateNewGroupFsys( const OUString
& rGroupName
, Content
& aGroup
)
1322 OUString aResultURL
;
1324 if ( maTemplateDirs
.hasElements() )
1326 OUString aTargetPath
= maTemplateDirs
[ maTemplateDirs
.getLength() - 1 ];
1328 // create a new folder with the given name
1330 OUString aNewFolderName
;
1332 // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1333 if ( !CreateNewUniqueFolderWithPrefix( aTargetPath
,
1338 && !CreateNewUniqueFolderWithPrefix( aTargetPath
,
1346 if ( !UpdateUINamesForTemplateDir_Impl( aTargetPath
, rGroupName
, aNewFolderName
) )
1348 // we could not create the groupuinames for the folder, so we delete the group in
1349 // the folder and return
1350 removeContent( aNewFolder
);
1354 // Now set the target url for this group and we are done
1355 Any
aValue( aResultURL
);
1357 if ( ! setProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1359 removeContent( aNewFolder
);
1368 sal_Bool
SfxDocTplService::addGroup( const OUString
& rGroupName
)
1373 ::osl::MutexGuard
aGuard( maMutex
);
1375 // Check, whether or not there is a group with this name
1377 OUString aNewGroupURL
;
1378 INetURLObject
aNewGroupObj( maRootURL
);
1380 aNewGroupObj
.insertName( rGroupName
, false,
1381 INetURLObject::LAST_SEGMENT
,
1382 INetURLObject::EncodeMechanism::All
);
1384 aNewGroupURL
= aNewGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1386 if ( Content::create( aNewGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aNewGroup
) ||
1387 ! createFolder( aNewGroupURL
, false, false, aNewGroup
) )
1389 // if there already was a group with this name or the new group
1390 // could not be created, we return here
1394 // Get the user template path entry ( new group will always
1395 // be added in the user template path )
1399 nIndex
= maTemplateDirs
.getLength();
1403 return false; // We don't know where to add the group
1405 aUserPath
= maTemplateDirs
[ nIndex
];
1407 // create a new folder with the given name
1409 OUString aNewFolderName
;
1410 OUString aNewFolderURL
;
1412 // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1413 if ( !CreateNewUniqueFolderWithPrefix( aUserPath
,
1418 && !CreateNewUniqueFolderWithPrefix( aUserPath
,
1424 // we could not create the folder, so we delete the group in the
1425 // hierarchy and return
1426 removeContent( aNewGroup
);
1430 if ( !UpdateUINamesForTemplateDir_Impl( aUserPath
, rGroupName
, aNewFolderName
) )
1432 // we could not create the groupuinames for the folder, so we delete the group in the
1433 // hierarchy, the folder and return
1434 removeContent( aNewGroup
);
1435 removeContent( aNewFolder
);
1439 // Now set the target url for this group and we are done
1440 Any
aValue( aNewFolderURL
);
1442 if ( ! setProperty( aNewGroup
, TARGET_DIR_URL
, aValue
) )
1444 removeContent( aNewGroup
);
1445 removeContent( aNewFolder
);
1453 sal_Bool
SfxDocTplService::removeGroup( const OUString
& rGroupName
)
1455 // remove all the elements that have the prefix aTargetURL
1456 // if the group does not have other elements remove it
1461 ::osl::MutexGuard
aGuard( maMutex
);
1463 bool bResult
= false;
1465 // create the group url
1466 INetURLObject
aGroupObj( maRootURL
);
1467 aGroupObj
.insertName( rGroupName
, false,
1468 INetURLObject::LAST_SEGMENT
,
1469 INetURLObject::EncodeMechanism::All
);
1471 // Get the target url
1473 const OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1475 if ( Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1477 const OUString
aPropName( TARGET_DIR_URL
);
1480 OUString aGroupTargetURL
;
1481 if ( getProperty( aGroup
, aPropName
, aValue
) )
1482 aValue
>>= aGroupTargetURL
;
1484 if ( aGroupTargetURL
.isEmpty() )
1485 return false; // nothing is allowed to be removed
1487 if ( !maTemplateDirs
.hasElements() )
1490 // check that the fs location is in writable folder and this is not a "My templates" folder
1491 INetURLObject
aGroupParentFolder( aGroupTargetURL
);
1492 if (!aGroupParentFolder
.removeSegment())
1495 OUString aGeneralTempPath
= findParentTemplateDir(
1496 aGroupParentFolder
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1498 if (aGeneralTempPath
.isEmpty())
1501 // now get the content of the Group
1502 uno::Reference
< XResultSet
> xResultSet
;
1503 Sequence
< OUString
> aProps
{ TARGET_URL
};
1507 xResultSet
= aGroup
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
1509 if ( xResultSet
.is() )
1511 bool bHasNonRemovable
= false;
1512 bool bHasShared
= false;
1514 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY_THROW
);
1515 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY_THROW
);
1517 while ( xResultSet
->next() )
1519 OUString
aTemplTargetURL( xRow
->getString( 1 ) );
1520 OUString aHierURL
= xContentAccess
->queryContentIdentifierString();
1522 if ( ::utl::UCBContentHelper::IsSubPath( aGroupTargetURL
, aTemplTargetURL
) )
1524 // this is a user template, and it can be removed
1525 if ( removeContent( aTemplTargetURL
) )
1526 removeContent( aHierURL
);
1528 bHasNonRemovable
= true;
1534 if ( !bHasNonRemovable
&& !bHasShared
)
1536 if ( removeContent( aGroupTargetURL
)
1537 || !::utl::UCBContentHelper::Exists( aGroupTargetURL
) )
1539 removeContent( aGroupURL
);
1540 RemoveUINamesForTemplateDir_Impl( aGeneralTempPath
, rGroupName
);
1541 bResult
= true; // the operation is successful only if the whole group is removed
1544 else if ( !bHasNonRemovable
)
1546 if ( removeContent( aGroupTargetURL
)
1547 || !::utl::UCBContentHelper::Exists( aGroupTargetURL
) )
1549 RemoveUINamesForTemplateDir_Impl( aGeneralTempPath
, rGroupName
);
1550 setProperty( aGroup
, aPropName
, uno::Any( OUString() ) );
1555 catch ( Exception
& ) {}
1562 sal_Bool
SfxDocTplService::renameGroup( const OUString
& rOldName
,
1563 const OUString
& rNewName
)
1565 if ( rOldName
== rNewName
)
1571 ::osl::MutexGuard
aGuard( maMutex
);
1573 // create the group url
1575 INetURLObject
aGroupObj( maRootURL
);
1576 aGroupObj
.insertName( rNewName
, false,
1577 INetURLObject::LAST_SEGMENT
,
1578 INetURLObject::EncodeMechanism::All
);
1579 OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1581 // Check, if there is a group with the new name, return false
1583 if ( Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1586 aGroupObj
.removeSegment();
1587 aGroupObj
.insertName( rOldName
, false,
1588 INetURLObject::LAST_SEGMENT
,
1589 INetURLObject::EncodeMechanism::All
);
1590 aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1592 // When there is no group with the old name, we can't rename it
1593 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1596 OUString aGroupTargetURL
;
1597 // there is no need to check whether target dir url is in target path, since if the target path is changed
1598 // the target dir url should be already generated new
1600 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1601 aValue
>>= aGroupTargetURL
;
1603 if ( aGroupTargetURL
.isEmpty() )
1606 if ( !maTemplateDirs
.hasElements() )
1609 // check that the fs location is in writable folder and this is not a "My templates" folder
1610 INetURLObject
aGroupParentFolder( aGroupTargetURL
);
1611 if (!aGroupParentFolder
.removeSegment() ||
1612 isInternalTemplateDir(aGroupParentFolder
.GetMainURL(INetURLObject::DecodeMechanism::NONE
)))
1617 // check that the group can be renamed ( all the contents must be in target location )
1618 bool bCanBeRenamed
= false;
1621 uno::Reference
< XResultSet
> xResultSet
;
1622 Sequence
< OUString
> aProps
{ TARGET_URL
};
1623 xResultSet
= aGroup
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
1625 if ( xResultSet
.is() )
1627 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY_THROW
);
1628 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY_THROW
);
1630 while ( xResultSet
->next() )
1632 if ( !::utl::UCBContentHelper::IsSubPath( aGroupTargetURL
, xRow
->getString( 1 ) ) )
1633 throw uno::Exception("not sub path", nullptr);
1636 bCanBeRenamed
= true;
1639 catch ( Exception
& ) {}
1641 if ( bCanBeRenamed
)
1643 INetURLObject
aGroupTargetObj( aGroupTargetURL
);
1644 const OUString aFsysName
= aGroupTargetObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
1646 if ( aGroupTargetObj
.removeSegment()
1647 && ReplaceUINamesForTemplateDir_Impl( aGroupTargetObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
1652 // rename the group in the hierarchy
1654 aTitleValue
<<= rNewName
;
1656 return setProperty( aGroup
, TITLE
, aTitleValue
);
1664 sal_Bool
SfxDocTplService::storeTemplate( const OUString
& rGroupName
,
1665 const OUString
& rTemplateName
,
1666 const uno::Reference
< frame::XStorable
>& rStorable
)
1671 ::osl::MutexGuard
aGuard( maMutex
);
1673 // Check, whether or not there is a group with this name
1674 // Return false, if there is no group with the given name
1675 Content aGroup
, aTemplateToRemove
;
1676 INetURLObject
aGroupObj( maRootURL
);
1677 bool bRemoveOldTemplateContent
= false;
1679 aGroupObj
.insertName( rGroupName
, false,
1680 INetURLObject::LAST_SEGMENT
,
1681 INetURLObject::EncodeMechanism::All
);
1682 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1684 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1687 OUString aGroupTargetURL
;
1689 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1690 aValue
>>= aGroupTargetURL
;
1693 // Check, if there's a template with the given name in this group
1694 // the target template should be overwritten if it is imported by user
1695 // in case the template is installed by office installation of by an add-in
1696 // it can not be replaced
1697 aGroupObj
.insertName( rTemplateName
, false,
1698 INetURLObject::LAST_SEGMENT
,
1699 INetURLObject::EncodeMechanism::All
);
1700 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1702 OUString aTemplateToRemoveTargetURL
;
1704 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplateToRemove
) )
1706 bRemoveOldTemplateContent
= true;
1707 if ( getProperty( aTemplateToRemove
, TARGET_URL
, aValue
) )
1708 aValue
>>= aTemplateToRemoveTargetURL
;
1710 if ( aGroupTargetURL
.isEmpty() || !maTemplateDirs
.hasElements()
1711 || (!aTemplateToRemoveTargetURL
.isEmpty() && isInternalTemplateDir(aTemplateToRemoveTargetURL
)) )
1712 return false; // it is not allowed to remove the template
1717 uno::Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
1719 // get document service name
1720 uno::Reference
< frame::XModuleManager2
> xModuleManager( frame::ModuleManager::create(xContext
) );
1721 const OUString sDocServiceName
{xModuleManager
->identify( uno::Reference
< uno::XInterface
>( rStorable
, uno::UNO_QUERY
) )};
1722 if ( sDocServiceName
.isEmpty() )
1723 throw uno::RuntimeException();
1725 // get the actual filter name
1726 uno::Reference
< lang::XMultiServiceFactory
> xConfigProvider
=
1727 configuration::theDefaultProvider::get( xContext
);
1729 uno::Sequence
<uno::Any
> aArgs(comphelper::InitAnyPropertySequence(
1731 {"nodepath", uno::Any(OUString( "/org.openoffice.Setup/Office/Factories/" ))}
1733 uno::Reference
< container::XNameAccess
> xSOFConfig(
1734 xConfigProvider
->createInstanceWithArguments(
1735 "com.sun.star.configuration.ConfigurationAccess",
1737 uno::UNO_QUERY_THROW
);
1739 uno::Reference
< container::XNameAccess
> xApplConfig
;
1740 xSOFConfig
->getByName( sDocServiceName
) >>= xApplConfig
;
1741 if ( !xApplConfig
.is() )
1742 throw uno::RuntimeException();
1744 OUString aFilterName
;
1745 xApplConfig
->getByName("ooSetupFactoryActualTemplateFilter") >>= aFilterName
;
1746 if ( aFilterName
.isEmpty() )
1747 throw uno::RuntimeException();
1749 // find the related type name
1750 uno::Reference
< container::XNameAccess
> xFilterFactory(
1751 mxContext
->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", mxContext
),
1752 uno::UNO_QUERY_THROW
);
1754 uno::Sequence
< beans::PropertyValue
> aFilterData
;
1755 xFilterFactory
->getByName( aFilterName
) >>= aFilterData
;
1757 for ( const auto& rProp
: std::as_const(aFilterData
) )
1758 if ( rProp
.Name
== "Type" )
1759 rProp
.Value
>>= aTypeName
;
1761 if ( aTypeName
.isEmpty() )
1762 throw uno::RuntimeException();
1764 // find the mediatype and extension
1765 uno::Reference
< container::XNameAccess
> xTypeDetection
=
1767 uno::Reference
< container::XNameAccess
>( mxType
, uno::UNO_QUERY_THROW
) :
1768 uno::Reference
< container::XNameAccess
>(
1769 mxContext
->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", mxContext
),
1770 uno::UNO_QUERY_THROW
);
1772 SequenceAsHashMap
aTypeProps( xTypeDetection
->getByName( aTypeName
) );
1773 uno::Sequence
< OUString
> aAllExt
=
1774 aTypeProps
.getUnpackedValueOrDefault("Extensions", Sequence
< OUString
>() );
1775 if ( !aAllExt
.hasElements() )
1776 throw uno::RuntimeException();
1778 const OUString aMediaType
{aTypeProps
.getUnpackedValueOrDefault("MediaType", OUString() )};
1779 const OUString aExt
{aAllExt
[0]};
1781 if ( aMediaType
.isEmpty() || aExt
.isEmpty() )
1782 throw uno::RuntimeException();
1784 // construct destination url
1785 if ( aGroupTargetURL
.isEmpty() )
1787 aGroupTargetURL
= CreateNewGroupFsys( rGroupName
, aGroup
);
1789 if ( aGroupTargetURL
.isEmpty() )
1790 throw uno::RuntimeException();
1793 OUString aNewTemplateTargetURL
= CreateNewUniqueFileWithPrefix( aGroupTargetURL
, rTemplateName
, aExt
);
1794 if ( aNewTemplateTargetURL
.isEmpty() )
1796 aNewTemplateTargetURL
= CreateNewUniqueFileWithPrefix( aGroupTargetURL
, "UserTemplate", aExt
);
1798 if ( aNewTemplateTargetURL
.isEmpty() )
1799 throw uno::RuntimeException();
1803 uno::Sequence
< PropertyValue
> aStoreArgs
{
1804 comphelper::makePropertyValue("FilterName", aFilterName
),
1805 comphelper::makePropertyValue("DocumentTitle", rTemplateName
)
1808 if( !::utl::UCBContentHelper::EqualURLs( aNewTemplateTargetURL
, rStorable
->getLocation() ))
1809 rStorable
->storeToURL( aNewTemplateTargetURL
, aStoreArgs
);
1813 // the storing was successful, now the old template with the same name can be removed if it existed
1814 if ( !aTemplateToRemoveTargetURL
.isEmpty() )
1816 removeContent( aTemplateToRemoveTargetURL
);
1820 * if the old template was the standard template
1821 * it is necessary to change the standard template with the new file name
1823 const OUString sStdTmplFile
= SfxObjectFactory::GetStandardTemplate( sDocServiceName
);
1824 if ( INetURLObject( sStdTmplFile
) == INetURLObject( aTemplateToRemoveTargetURL
) )
1826 SfxObjectFactory::SetStandardTemplate( sDocServiceName
, aNewTemplateTargetURL
);
1830 if ( bRemoveOldTemplateContent
)
1831 removeContent( aTemplateToRemove
);
1833 // add the template to hierarchy
1834 return addEntry( aGroup
, rTemplateName
, aNewTemplateTargetURL
, aMediaType
);
1838 // the template was not stored
1844 sal_Bool
SfxDocTplService::addTemplate( const OUString
& rGroupName
,
1845 const OUString
& rTemplateName
,
1846 const OUString
& rSourceURL
)
1851 ::osl::MutexGuard
aGuard( maMutex
);
1853 // Check, whether or not there is a group with this name
1854 // Return false, if there is no group with the given name
1855 Content aGroup
, aTemplate
, aTargetGroup
;
1856 INetURLObject
aGroupObj( maRootURL
);
1858 aGroupObj
.insertName( rGroupName
, false,
1859 INetURLObject::LAST_SEGMENT
,
1860 INetURLObject::EncodeMechanism::All
);
1861 const OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1863 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1866 // Check, if there's a template with the given name in this group
1867 // Return false, if there already is a template
1868 aGroupObj
.insertName( rTemplateName
, false,
1869 INetURLObject::LAST_SEGMENT
,
1870 INetURLObject::EncodeMechanism::All
);
1871 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1873 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
1876 // get the target url of the group
1877 OUString aTargetURL
;
1880 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1881 aValue
>>= aTargetURL
;
1883 if ( aTargetURL
.isEmpty() )
1885 aTargetURL
= CreateNewGroupFsys( rGroupName
, aGroup
);
1887 if ( aTargetURL
.isEmpty() )
1891 // Get the content type
1892 OUString aTitle
, aType
;
1894 bool bDocHasTitle
= false;
1895 getTitleFromURL( rSourceURL
, aTitle
, aType
, bDocHasTitle
);
1897 INetURLObject
aSourceObj( rSourceURL
);
1898 if ( rTemplateName
== aTitle
)
1900 // addTemplate will sometimes be called just to add an entry in the
1901 // hierarchy; the target URL and the source URL will be the same in
1903 // TODO/LATER: get rid of this old hack
1905 INetURLObject
aTargetObj( aTargetURL
);
1907 aTargetObj
.insertName( rTemplateName
, false,
1908 INetURLObject::LAST_SEGMENT
,
1909 INetURLObject::EncodeMechanism::All
);
1910 aTargetObj
.setExtension( aSourceObj
.getExtension() );
1912 const OUString aTargetURL2
= aTargetObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1914 if ( aTargetURL2
== rSourceURL
)
1915 return addEntry( aGroup
, rTemplateName
, aTargetURL2
, aType
);
1918 // copy the template into the new group (targeturl)
1920 INetURLObject
aTmpURL( aSourceObj
);
1921 aTmpURL
.CutExtension();
1922 const OUString aPattern
{aTmpURL
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
)};
1924 const OUString aNewTemplateTargetURL
{CreateNewUniqueFileWithPrefix( aTargetURL
, aPattern
, aSourceObj
.getExtension() )};
1925 INetURLObject
aNewTemplateTargetObj( aNewTemplateTargetURL
);
1926 const OUString aNewTemplateTargetName
{aNewTemplateTargetObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
)};
1927 if ( aNewTemplateTargetURL
.isEmpty() || aNewTemplateTargetName
.isEmpty() )
1930 // get access to source file
1931 Content aSourceContent
;
1932 uno::Reference
< ucb::XCommandEnvironment
> xEnv
;
1933 INetURLObject
aSourceURL( rSourceURL
);
1934 if( ! Content::create( aSourceURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), xEnv
, comphelper::getProcessComponentContext(), aSourceContent
) )
1937 if( ! Content::create( aTargetURL
, xEnv
, comphelper::getProcessComponentContext(), aTargetGroup
) )
1940 // transfer source file
1943 aTargetGroup
.transferContent( aSourceContent
,
1944 InsertOperation::Copy
,
1945 aNewTemplateTargetName
,
1946 NameClash::OVERWRITE
,
1949 // allow to edit the added template
1950 Content aResultContent
;
1951 if ( Content::create( aNewTemplateTargetURL
, xEnv
, comphelper::getProcessComponentContext(), aResultContent
) )
1953 static constexpr OUString
aPropertyName( u
"IsReadOnly"_ustr
);
1955 bool bReadOnly
= false;
1956 if ( getProperty( aResultContent
, aPropertyName
, aProperty
) && ( aProperty
>>= bReadOnly
) && bReadOnly
)
1957 setProperty( aResultContent
, aPropertyName
, uno::Any( false ) );
1960 catch ( ContentCreationException
& )
1962 catch ( Exception
& )
1966 // either the document has title and it is the same as requested, or we have to set it
1967 bool bCorrectTitle
= ( bDocHasTitle
&& aTitle
== rTemplateName
);
1968 if ( !bCorrectTitle
)
1970 if ( !bDocHasTitle
)
1972 INetURLObject
aNewTmpObj( aNewTemplateTargetObj
);
1973 aNewTmpObj
.CutExtension();
1974 bCorrectTitle
= ( aNewTmpObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
) == rTemplateName
);
1977 if ( !bCorrectTitle
)
1978 bCorrectTitle
= setTitleForURL( aNewTemplateTargetURL
, rTemplateName
);
1981 if ( bCorrectTitle
)
1983 // create a new entry in the hierarchy
1984 return addEntry( aGroup
, rTemplateName
, aNewTemplateTargetURL
, aType
);
1987 // TODO/LATER: The user could be notified here that the renaming has failed
1988 // create a new entry in the hierarchy
1989 addEntry( aGroup
, aTitle
, aNewTemplateTargetURL
, aType
);
1993 bool SfxDocTplService::isInternalTemplateDir(const OUString
& rURL
) const
1995 return std::any_of(maInternalTemplateDirs
.begin(), maInternalTemplateDirs
.end(),
1996 [&rURL
](const OUString
& rDir
) { return ::utl::UCBContentHelper::IsSubPath(rDir
, rURL
); });
1999 OUString
SfxDocTplService::findParentTemplateDir(const OUString
& rURL
) const
2001 const OUString
* pDirs
= std::find_if(maTemplateDirs
.begin(), maTemplateDirs
.end(),
2002 [&rURL
](const OUString
& rDir
) { return ::utl::UCBContentHelper::IsSubPath(rDir
, rURL
); });
2003 if (pDirs
!= maTemplateDirs
.end())
2008 sal_Bool
SfxDocTplService::removeTemplate( const OUString
& rGroupName
,
2009 const OUString
& rTemplateName
)
2013 ::osl::MutexGuard
aGuard( maMutex
);
2015 // Check, whether or not there is a group with this name
2016 // Return false, if there is no group with the given name
2017 Content aGroup
, aTemplate
;
2018 INetURLObject
aGroupObj( maRootURL
);
2020 aGroupObj
.insertName( rGroupName
, false,
2021 INetURLObject::LAST_SEGMENT
,
2022 INetURLObject::EncodeMechanism::All
);
2023 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2025 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2028 // Check, if there's a template with the given name in this group
2029 // Return false, if there is no template
2030 aGroupObj
.insertName( rTemplateName
, false,
2031 INetURLObject::LAST_SEGMENT
,
2032 INetURLObject::EncodeMechanism::All
);
2033 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2035 if ( !Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2038 // get the target URL from the template
2039 OUString aTargetURL
;
2042 if ( getProperty( aTemplate
, TARGET_URL
, aValue
) )
2043 aValue
>>= aTargetURL
;
2045 // delete the target template
2046 if ( !aTargetURL
.isEmpty() )
2048 if (isInternalTemplateDir(aTargetURL
))
2051 removeContent( aTargetURL
);
2054 // delete the template entry
2055 return removeContent( aTemplate
);
2059 sal_Bool
SfxDocTplService::renameTemplate( const OUString
& rGroupName
,
2060 const OUString
& rOldName
,
2061 const OUString
& rNewName
)
2063 if ( rOldName
== rNewName
)
2068 ::osl::MutexGuard
aGuard( maMutex
);
2070 // Check, whether or not there is a group with this name
2071 // Return false, if there is no group with the given name
2072 Content aGroup
, aTemplate
;
2073 INetURLObject
aGroupObj( maRootURL
);
2075 aGroupObj
.insertName( rGroupName
, false,
2076 INetURLObject::LAST_SEGMENT
,
2077 INetURLObject::EncodeMechanism::All
);
2078 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2080 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2083 // Check, if there's a template with the new name in this group
2084 // Return false, if there is one
2085 aGroupObj
.insertName( rNewName
, false,
2086 INetURLObject::LAST_SEGMENT
,
2087 INetURLObject::EncodeMechanism::All
);
2088 OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2090 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2093 // Check, if there's a template with the old name in this group
2094 // Return false, if there is no template
2095 aGroupObj
.removeSegment();
2096 aGroupObj
.insertName( rOldName
, false,
2097 INetURLObject::LAST_SEGMENT
,
2098 INetURLObject::EncodeMechanism::All
);
2099 aTemplateURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2101 if ( !Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2104 OUString aTemplateTargetURL
;
2107 if ( getProperty( aTemplate
, TARGET_URL
, aTargetValue
) )
2108 aTargetValue
>>= aTemplateTargetURL
;
2110 if ( !setTitleForURL( aTemplateTargetURL
, rNewName
) )
2113 // rename the template entry in the cache
2115 aTitleValue
<<= rNewName
;
2117 return setProperty( aTemplate
, TITLE
, aTitleValue
);
2122 //--- XDocumentTemplates ---
2124 uno::Reference
< ucb::XContent
> SAL_CALL
SfxDocTplService::getContent()
2127 return maRootContent
.get();
2132 WaitWindow_Impl::WaitWindow_Impl() : WorkWindow(nullptr, WB_BORDER
| WB_3DLOOK
)
2134 tools::Rectangle
aRect(0, 0, 300, 30000);
2135 maText
= SfxResId(RID_CNT_STR_WAITING
);
2136 maRect
= GetTextRect(aRect
, maText
, gnTextStyle
);
2138 aRect
.AdjustRight(2 * X_OFFSET
);
2139 aRect
.AdjustBottom(2 * Y_OFFSET
);
2140 maRect
.SetPos(Point(X_OFFSET
, Y_OFFSET
));
2141 SetOutputSizePixel(aRect
.GetSize());
2145 GetOutDev()->Flush();
2149 WaitWindow_Impl::~WaitWindow_Impl()
2154 void WaitWindow_Impl::dispose()
2157 WorkWindow::dispose();
2161 void WaitWindow_Impl::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*rRect*/)
2163 rRenderContext
.DrawText(maRect
, maText
, gnTextStyle
);
2166 void SfxDocTplService::addHierGroup( GroupList_Impl
& rList
,
2167 const OUString
& rTitle
,
2168 const OUString
& rOwnURL
)
2170 // now get the content of the Group
2172 uno::Reference
<XResultSet
> xResultSet
;
2176 aContent
= Content(rOwnURL
, maCmdEnv
, comphelper::getProcessComponentContext());
2177 xResultSet
= aContent
.createCursor( { TITLE
, TARGET_URL
, PROPERTY_TYPE
}, INCLUDE_DOCUMENTS_ONLY
);
2179 catch (ContentCreationException
&)
2181 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
2183 catch (Exception
&) {}
2185 if ( !xResultSet
.is() )
2188 GroupData_Impl
*pGroup
= new GroupData_Impl( rTitle
);
2189 pGroup
->setHierarchy( true );
2190 pGroup
->setHierarchyURL( rOwnURL
);
2191 rList
.push_back( std::unique_ptr
<GroupData_Impl
>(pGroup
) );
2193 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2194 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2198 while ( xResultSet
->next() )
2200 bool bUpdateType
= false;
2201 DocTemplates_EntryData_Impl
*pData
;
2203 const OUString
aTitle( xRow
->getString( 1 ) );
2204 const OUString
aTargetDir( xRow
->getString( 2 ) );
2205 OUString
aType( xRow
->getString( 3 ) );
2206 const OUString aHierURL
{xContentAccess
->queryContentIdentifierString()};
2208 if ( aType
.isEmpty() )
2212 bool bDocHasTitle
= false;
2213 getTitleFromURL( aTargetDir
, aTmpTitle
, aType
, bDocHasTitle
);
2215 if ( !aType
.isEmpty() )
2219 pData
= pGroup
->addEntry( aTitle
, aTargetDir
, aType
, aHierURL
);
2220 pData
->setUpdateType( bUpdateType
);
2223 catch ( Exception
& ) {}
2227 void SfxDocTplService::addFsysGroup( GroupList_Impl
& rList
,
2228 const OUString
& rTitle
,
2229 const OUString
& rUITitle
,
2230 const OUString
& rOwnURL
,
2231 bool bWriteableGroup
)
2235 if ( rUITitle
.isEmpty() )
2237 // reserved FS names that should not be used
2238 if ( rTitle
== "wizard" )
2240 else if ( rTitle
== "internal" )
2243 aTitle
= getLongName( rTitle
);
2248 if ( aTitle
.isEmpty() )
2251 GroupData_Impl
* pGroup
= nullptr;
2252 for (const std::unique_ptr
<GroupData_Impl
>& i
: rList
)
2254 if ( i
->getTitle() == aTitle
)
2263 pGroup
= new GroupData_Impl( aTitle
);
2264 rList
.push_back( std::unique_ptr
<GroupData_Impl
>(pGroup
) );
2267 if ( bWriteableGroup
)
2268 pGroup
->setTargetURL( rOwnURL
);
2272 // now get the content of the Group
2274 uno::Reference
< XResultSet
> xResultSet
;
2275 Sequence
< OUString
> aProps
{ TITLE
};
2279 // this method is only used during checking of the available template-folders
2280 // that should happen quietly
2281 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
2282 aContent
= Content( rOwnURL
, aQuietEnv
, comphelper::getProcessComponentContext() );
2283 xResultSet
= aContent
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
2285 catch ( Exception
& ) {}
2287 if ( !xResultSet
.is() )
2290 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2291 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2295 while ( xResultSet
->next() )
2297 OUString
aChildTitle( xRow
->getString( 1 ) );
2298 const OUString aTargetURL
{xContentAccess
->queryContentIdentifierString()};
2301 if ( aChildTitle
== "sfx.tlx" || aChildTitle
== "groupuinames.xml" )
2304 bool bDocHasTitle
= false;
2305 getTitleFromURL( aTargetURL
, aChildTitle
, aType
, bDocHasTitle
);
2307 pGroup
->addEntry( aChildTitle
, aTargetURL
, aType
, OUString() );
2310 catch ( Exception
& ) {}
2314 void SfxDocTplService::createFromContent( GroupList_Impl
& rList
,
2317 bool bWriteableContent
)
2319 const OUString aTargetURL
{rContent
.get()->getIdentifier()->getContentIdentifier()};
2321 // when scanning the file system, we have to add the 'standard' group, too
2324 const OUString aUIStdTitle
{getLongName( STANDARD_FOLDER
)};
2325 addFsysGroup( rList
, OUString(), aUIStdTitle
, aTargetURL
, bWriteableContent
);
2328 // search for predefined UI names
2329 INetURLObject
aLayerObj( aTargetURL
);
2331 // TODO/LATER: Use hashmap in future
2332 std::vector
< beans::StringPair
> aUINames
;
2334 aUINames
= ReadUINamesForTemplateDir_Impl( aLayerObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) );
2336 uno::Reference
< XResultSet
> xResultSet
;
2337 Sequence
< OUString
> aProps
{ TITLE
};
2341 xResultSet
= rContent
.createCursor( aProps
, INCLUDE_FOLDERS_ONLY
);
2343 catch ( Exception
& ) {}
2345 if ( !xResultSet
.is() )
2348 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2349 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2353 while ( xResultSet
->next() )
2355 // TODO/LATER: clarify the encoding of the Title
2356 const OUString
aTitle( xRow
->getString( 1 ) );
2357 const OUString
aTargetSubfolderURL( xContentAccess
->queryContentIdentifierString() );
2360 addHierGroup( rList
, aTitle
, aTargetSubfolderURL
);
2364 for (const beans::StringPair
& rUIName
: aUINames
)
2365 if ( rUIName
.First
== aTitle
)
2367 aUITitle
= rUIName
.Second
;
2371 addFsysGroup( rList
, aTitle
, aUITitle
, aTargetSubfolderURL
, bWriteableContent
);
2375 catch ( Exception
& ) {}
2379 void SfxDocTplService::removeFromHierarchy( DocTemplates_EntryData_Impl
const *pData
)
2383 if ( Content::create( pData
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2385 removeContent( aTemplate
);
2390 void SfxDocTplService::addToHierarchy( GroupData_Impl
const *pGroup
,
2391 DocTemplates_EntryData_Impl
const *pData
)
2393 Content aGroup
, aTemplate
;
2395 if ( ! Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2398 // Check, if there's a template with the given name in this group
2399 // Return if there is already a template
2400 INetURLObject
aGroupObj( pGroup
->getHierarchyURL() );
2402 aGroupObj
.insertName( pData
->getTitle(), false,
2403 INetURLObject::LAST_SEGMENT
,
2404 INetURLObject::EncodeMechanism::All
);
2406 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2408 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2411 addEntry( aGroup
, pData
->getTitle(),
2412 pData
->getTargetURL(),
2417 void SfxDocTplService::updateData( DocTemplates_EntryData_Impl
const *pData
)
2421 if ( ! Content::create( pData
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2424 if ( pData
->getUpdateType() )
2426 setProperty( aTemplate
, PROPERTY_TYPE
, Any( pData
->getType() ) );
2429 if ( pData
->getUpdateLink() )
2431 setProperty( aTemplate
, TARGET_URL
, Any( pData
->getTargetURL() ) );
2436 void SfxDocTplService::addGroupToHierarchy( GroupData_Impl
*pGroup
)
2440 INetURLObject
aNewGroupObj( maRootURL
);
2441 aNewGroupObj
.insertName( pGroup
->getTitle(), false,
2442 INetURLObject::LAST_SEGMENT
,
2443 INetURLObject::EncodeMechanism::All
);
2445 const OUString aNewGroupURL
{aNewGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2447 if ( createFolder( aNewGroupURL
, false, false, aGroup
) )
2449 setProperty( aGroup
, TARGET_DIR_URL
, Any( pGroup
->getTargetURL() ) );
2450 pGroup
->setHierarchyURL( aNewGroupURL
);
2452 size_t nCount
= pGroup
->count();
2453 for ( size_t i
= 0; i
< nCount
; i
++ )
2455 DocTemplates_EntryData_Impl
*pData
= pGroup
->getEntry( i
);
2456 addToHierarchy( pGroup
, pData
); // add entry to hierarchy
2462 void SfxDocTplService::removeFromHierarchy( GroupData_Impl
const *pGroup
)
2466 if ( Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2468 removeContent( aGroup
);
2473 GroupData_Impl::GroupData_Impl( OUString aTitle
)
2474 : maTitle(std::move(aTitle
)), mbInUse(false), mbInHierarchy(false)
2479 DocTemplates_EntryData_Impl
* GroupData_Impl::addEntry( const OUString
& rTitle
,
2480 const OUString
& rTargetURL
,
2481 const OUString
& rType
,
2482 const OUString
& rHierURL
)
2484 DocTemplates_EntryData_Impl
* pData
= nullptr;
2485 bool EntryFound
= false;
2487 for (auto const & p
: maEntries
)
2490 if ( pData
->getTitle() == rTitle
)
2499 pData
= new DocTemplates_EntryData_Impl( rTitle
);
2500 pData
->setTargetURL( rTargetURL
);
2501 pData
->setType( rType
);
2502 if ( !rHierURL
.isEmpty() )
2504 pData
->setHierarchyURL( rHierURL
);
2505 pData
->setHierarchy( true );
2507 maEntries
.emplace_back( pData
);
2511 if ( !rHierURL
.isEmpty() )
2513 pData
->setHierarchyURL( rHierURL
);
2514 pData
->setHierarchy( true );
2517 if ( pData
->getInHierarchy() )
2520 if ( rTargetURL
!= pData
->getTargetURL() )
2522 pData
->setTargetURL( rTargetURL
);
2523 pData
->setUpdateLink( true );
2531 DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( OUString aTitle
)
2532 : maTitle(std::move(aTitle
)), mbInHierarchy(false), mbInUse(false), mbUpdateType(false), mbUpdateLink(false)
2539 bool SfxURLRelocator_Impl::propertyCanContainOfficeDir(
2540 std::u16string_view rPropName
)
2542 // Note: TargetURL is handled by UCB itself (because it is a property
2543 // with a predefined semantic). Additional Core properties introduced
2544 // be a client app must be handled by the client app itself, because
2545 // the UCB does not know the semantics of those properties.
2546 return ( rPropName
== TARGET_DIR_URL
|| rPropName
== PROPERTY_DIRLIST
);
2550 SfxURLRelocator_Impl::SfxURLRelocator_Impl( uno::Reference
< XComponentContext
> xContext
)
2551 : mxContext(std::move( xContext
))
2556 SfxURLRelocator_Impl::~SfxURLRelocator_Impl()
2561 void SfxURLRelocator_Impl::initOfficeInstDirs()
2563 if ( !mxOfficeInstDirs
.is() )
2565 std::scoped_lock
aGuard( maMutex
);
2566 if ( !mxOfficeInstDirs
.is() )
2568 OSL_ENSURE( mxContext
.is(), "No service manager!" );
2570 mxOfficeInstDirs
= theOfficeInstallationDirectories::get(mxContext
);
2576 void SfxURLRelocator_Impl::implExpandURL( OUString
& io_url
)
2578 const INetURLObject
aParser( io_url
);
2579 if ( aParser
.GetProtocol() != INetProtocol::VndSunStarExpand
)
2582 io_url
= aParser
.GetURLPath( INetURLObject::DecodeMechanism::WithCharset
);
2585 if ( !mxMacroExpander
.is() )
2587 mxMacroExpander
.set( theMacroExpander::get(mxContext
), UNO_SET_THROW
);
2589 io_url
= mxMacroExpander
->expandMacros( io_url
);
2591 catch( const Exception
& )
2593 DBG_UNHANDLED_EXCEPTION("sfx.doc");
2598 void SfxURLRelocator_Impl::makeRelocatableURL( OUString
& rURL
)
2600 if ( !rURL
.isEmpty() )
2602 initOfficeInstDirs();
2603 implExpandURL( rURL
);
2604 rURL
= mxOfficeInstDirs
->makeRelocatableURL( rURL
);
2609 void SfxURLRelocator_Impl::makeAbsoluteURL( OUString
& rURL
)
2611 if ( !rURL
.isEmpty() )
2613 initOfficeInstDirs();
2614 implExpandURL( rURL
);
2615 rURL
= mxOfficeInstDirs
->makeAbsoluteURL( rURL
);
2619 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
2620 com_sun_star_comp_sfx2_DocumentTemplates_get_implementation(
2621 css::uno::XComponentContext
*context
,
2622 css::uno::Sequence
<css::uno::Any
> const &)
2624 return cppu::acquire(new SfxDocTplService(context
));
2627 OUString
DocTemplLocaleHelper::GetStandardGroupString()
2629 return SfxResId(TEMPLATE_LONG_NAMES_ARY
[0]);
2632 std::vector
<OUString
> DocTemplLocaleHelper::GetBuiltInGroupNames()
2634 std::vector
<OUString
> aGroups
;
2635 for(auto const & aGroupName
: TEMPLATE_LONG_NAMES_ARY
)
2636 aGroups
.push_back(SfxResId(aGroupName
));
2640 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */