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/sequenceashashmap.hxx>
31 #include <comphelper/storagehelper.hxx>
32 #include <comphelper/string.hxx>
33 #include <cppuhelper/implbase.hxx>
34 #include <cppuhelper/supportsservice.hxx>
35 #include <com/sun/star/beans/IllegalTypeException.hpp>
36 #include <com/sun/star/beans/PropertyAttribute.hpp>
37 #include <com/sun/star/beans/PropertyExistException.hpp>
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/beans/XPropertySetInfo.hpp>
40 #include <com/sun/star/beans/XPropertyContainer.hpp>
41 #include <com/sun/star/beans/StringPair.hpp>
42 #include <com/sun/star/util/theMacroExpander.hpp>
43 #include <com/sun/star/util/theOfficeInstallationDirectories.hpp>
44 #include <com/sun/star/configuration/theDefaultProvider.hpp>
45 #include <com/sun/star/document/XTypeDetection.hpp>
46 #include <com/sun/star/document/DocumentProperties.hpp>
47 #include <com/sun/star/io/TempFile.hpp>
48 #include <com/sun/star/sdbc/XResultSet.hpp>
49 #include <com/sun/star/sdbc/XRow.hpp>
50 #include <com/sun/star/ucb/ContentCreationException.hpp>
51 #include <com/sun/star/ucb/NameClash.hpp>
52 #include <com/sun/star/ucb/NameClashException.hpp>
53 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
54 #include <com/sun/star/ucb/XContentAccess.hpp>
55 #include <com/sun/star/frame/ModuleManager.hpp>
56 #include <com/sun/star/uno/Exception.hpp>
57 #include <com/sun/star/task/InteractionHandler.hpp>
58 #include <com/sun/star/ucb/XProgressHandler.hpp>
59 #include <com/sun/star/container/XNameAccess.hpp>
60 #include <com/sun/star/frame/XDocumentTemplates.hpp>
61 #include <com/sun/star/frame/XStorable.hpp>
62 #include <com/sun/star/lang/Locale.hpp>
63 #include <com/sun/star/lang/XLocalizable.hpp>
64 #include <com/sun/star/lang/XServiceInfo.hpp>
65 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
66 #include <com/sun/star/ucb/XContent.hpp>
67 #include <com/sun/star/beans/PropertyValue.hpp>
68 #include <com/sun/star/uno/RuntimeException.hpp>
69 #include <com/sun/star/uno/XComponentContext.hpp>
70 #include <com/sun/star/util/thePathSettings.hpp>
72 #include <svtools/templatefoldercache.hxx>
73 #include <unotools/configmgr.hxx>
74 #include <unotools/ucbhelper.hxx>
75 #include <i18nlangtag/languagetag.hxx>
76 #include <ucbhelper/content.hxx>
78 #include <sfx2/sfxresid.hxx>
79 #include <sfxurlrelocator.hxx>
80 #include "doctemplateslocal.hxx"
81 #include <sfx2/docfac.hxx>
82 #include <sfx2/strings.hrc>
83 #include <doctempl.hrc>
88 #define SERVICENAME_TYPEDETECTION "com.sun.star.document.TypeDetection"
90 #define TEMPLATE_ROOT_URL "vnd.sun.star.hier:/templates"
92 #define IS_FOLDER "IsFolder"
93 #define IS_DOCUMENT "IsDocument"
94 #define TARGET_URL "TargetURL"
95 #define TEMPLATE_VERSION "TemplateComponentVersion"
96 #define TEMPLATE_VERSION_VALUE "2"
97 #define TYPE_FOLDER "application/vnd.sun.star.hier-folder"
98 #define TYPE_LINK "application/vnd.sun.star.hier-link"
99 #define TYPE_FSYS_FOLDER "application/vnd.sun.staroffice.fsys-folder"
100 #define TYPE_FSYS_FILE "application/vnd.sun.staroffice.fsys-file"
102 #define PROPERTY_DIRLIST "DirectoryList"
103 #define PROPERTY_NEEDSUPDATE "NeedsUpdate"
104 #define PROPERTY_TYPE "TypeDescription"
106 #define TARGET_DIR_URL "TargetDirURL"
107 #define COMMAND_DELETE "delete"
109 #define STANDARD_FOLDER "standard"
113 using namespace ::com::sun::star
;
114 using namespace ::com::sun::star::beans
;
115 using namespace ::com::sun::star::document
;
116 using namespace ::com::sun::star::io
;
117 using namespace ::com::sun::star::lang
;
118 using namespace ::com::sun::star::sdbc
;
119 using namespace ::com::sun::star::ucb
;
120 using namespace ::com::sun::star::uno
;
121 using namespace ::com::sun::star::container
;
122 using namespace ::com::sun::star::util
;
124 using namespace ::ucbhelper
;
125 using namespace ::comphelper
;
131 class WaitWindow_Impl
: public WorkWindow
133 tools::Rectangle maRect
;
135 static constexpr DrawTextFlags gnTextStyle
= DrawTextFlags::Center
| DrawTextFlags::VCenter
| DrawTextFlags::WordBreak
| DrawTextFlags::MultiLine
;
139 virtual ~WaitWindow_Impl() override
;
140 virtual void dispose() override
;
141 virtual void Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
) override
;
150 OUString maShortName
;
154 class DocTemplates_EntryData_Impl
;
155 class GroupData_Impl
;
157 typedef vector
< std::unique_ptr
<GroupData_Impl
> > GroupList_Impl
;
160 class TplTaskEnvironment
: public ::cppu::WeakImplHelper
< ucb::XCommandEnvironment
>
162 uno::Reference
< task::XInteractionHandler
> m_xInteractionHandler
;
165 explicit TplTaskEnvironment( const uno::Reference
< task::XInteractionHandler
>& rxInteractionHandler
)
166 : m_xInteractionHandler( rxInteractionHandler
)
169 virtual uno::Reference
<task::XInteractionHandler
> SAL_CALL
getInteractionHandler() override
170 { return m_xInteractionHandler
; }
172 virtual uno::Reference
<ucb::XProgressHandler
> SAL_CALL
getProgressHandler() override
173 { return uno::Reference
<ucb::XProgressHandler
>(); }
176 class SfxDocTplService_Impl
178 uno::Reference
< XComponentContext
> mxContext
;
179 uno::Reference
< XCommandEnvironment
> maCmdEnv
;
180 uno::Reference
< XDocumentProperties
> m_xDocProps
;
181 uno::Reference
< XTypeDetection
> mxType
;
183 ::osl::Mutex maMutex
;
184 Sequence
< OUString
> maTemplateDirs
;
185 Sequence
< OUString
> maInternalTemplateDirs
;
187 std::vector
< std::unique_ptr
<NamePair_Impl
> > maNames
;
188 lang::Locale maLocale
;
189 Content maRootContent
;
190 bool mbIsInitialized
: 1;
191 bool mbLocaleSet
: 1;
193 SfxURLRelocator_Impl maRelocator
;
196 void getDefaultLocale();
198 void readFolderList();
200 OUString
getLongName( const OUString
& rShortName
);
201 bool setTitleForURL( const OUString
& rURL
, const OUString
& aTitle
);
202 void getTitleFromURL( const OUString
& rURL
, OUString
& aTitle
, OUString
& aType
, bool& bDocHasTitle
);
204 bool addEntry( Content
& rParentFolder
,
205 const OUString
& rTitle
,
206 const OUString
& rTargetURL
,
207 const OUString
& rType
);
209 bool createFolder( const OUString
& rNewFolderURL
,
212 Content
&rNewFolder
);
214 static bool CreateNewUniqueFolderWithPrefix( const OUString
& aPath
,
215 const OUString
& aPrefix
,
216 OUString
& aNewFolderName
,
217 OUString
& aNewFolderURL
,
218 Content
& aNewFolder
);
219 static OUString
CreateNewUniqueFileWithPrefix( const OUString
& aPath
,
220 const OUString
& aPrefix
,
221 const OUString
& aExt
);
223 std::vector
< beans::StringPair
> ReadUINamesForTemplateDir_Impl( const OUString
& aUserPath
);
224 bool UpdateUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
225 const OUString
& aGroupName
,
226 const OUString
& aNewFolderName
);
227 bool ReplaceUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
228 const OUString
& aFsysGroupName
,
229 const OUString
& aOldGroupName
,
230 const OUString
& aNewGroupName
);
231 void RemoveUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
232 const OUString
& aGroupName
);
233 bool WriteUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
234 const std::vector
< beans::StringPair
>& aUINames
);
236 OUString
CreateNewGroupFsys( const OUString
& rGroupName
, Content
& aGroup
);
238 static bool removeContent( Content
& rContent
);
239 bool removeContent( const OUString
& rContentURL
);
241 bool setProperty( Content
& rContent
,
242 const OUString
& rPropName
,
243 const Any
& rPropValue
);
244 bool getProperty( Content
& rContent
,
245 const OUString
& rPropName
,
248 void createFromContent( GroupList_Impl
& rList
,
251 bool bWriteableContent
);
252 void addHierGroup( GroupList_Impl
& rList
,
253 const OUString
& rTitle
,
254 const OUString
& rOwnURL
);
255 void addFsysGroup( GroupList_Impl
& rList
,
256 const OUString
& rTitle
,
257 const OUString
& rUITitle
,
258 const OUString
& rOwnURL
,
259 bool bWriteableGroup
);
260 void removeFromHierarchy( DocTemplates_EntryData_Impl
const *pData
);
261 void addToHierarchy( GroupData_Impl
const *pGroup
,
262 DocTemplates_EntryData_Impl
const *pData
);
264 void removeFromHierarchy( GroupData_Impl
const *pGroup
);
265 void addGroupToHierarchy( GroupData_Impl
*pGroup
);
267 void updateData( DocTemplates_EntryData_Impl
const *pData
);
269 //See: #i66157# and rhbz#1065807
270 //return which template dir the rURL is a subpath of
271 OUString
findParentTemplateDir(const OUString
& rURL
) const;
273 //See: #i66157# and rhbz#1065807
274 //return true if rURL is a path (or subpath of) a dir which is not a user path
275 //which implies neither it or its contents can be removed
276 bool isInternalTemplateDir(const OUString
& rURL
) const;
278 explicit SfxDocTplService_Impl( const uno::Reference
< XComponentContext
> & xContext
);
279 ~SfxDocTplService_Impl();
281 bool init() { if ( !mbIsInitialized
) init_Impl(); return mbIsInitialized
; }
282 const Content
& getContent() const { return maRootContent
; }
284 void setLocale( const lang::Locale
& rLocale
);
285 lang::Locale
getLocale();
287 bool storeTemplate( const OUString
& rGroupName
,
288 const OUString
& rTemplateName
,
289 const uno::Reference
< frame::XStorable
>& rStorable
);
291 bool addTemplate( const OUString
& rGroupName
,
292 const OUString
& rTemplateName
,
293 const OUString
& rSourceURL
);
294 bool removeTemplate( const OUString
& rGroupName
,
295 const OUString
& rTemplateName
);
296 bool renameTemplate( const OUString
& rGroupName
,
297 const OUString
& rOldName
,
298 const OUString
& rNewName
);
300 bool addGroup( const OUString
& rGroupName
);
301 bool removeGroup( const OUString
& rGroupName
);
302 bool renameGroup( const OUString
& rOldName
,
303 const OUString
& rNewName
);
310 class DocTemplates_EntryData_Impl
314 OUString maTargetURL
;
315 OUString maHierarchyURL
;
317 bool mbInHierarchy
: 1;
319 bool mbUpdateType
: 1;
320 bool mbUpdateLink
: 1;
323 explicit DocTemplates_EntryData_Impl( const OUString
& rTitle
);
325 void setInUse() { mbInUse
= true; }
326 void setHierarchy( bool bInHierarchy
) { mbInHierarchy
= bInHierarchy
; }
327 void setUpdateLink( bool bUpdateLink
) { mbUpdateLink
= bUpdateLink
; }
328 void setUpdateType( bool bUpdateType
) { mbUpdateType
= bUpdateType
; }
330 bool getInUse() const { return mbInUse
; }
331 bool getInHierarchy() const { return mbInHierarchy
; }
332 bool getUpdateLink() const { return mbUpdateLink
; }
333 bool getUpdateType() const { return mbUpdateType
; }
335 const OUString
& getHierarchyURL() const { return maHierarchyURL
; }
336 const OUString
& getTargetURL() const { return maTargetURL
; }
337 const OUString
& getTitle() const { return maTitle
; }
338 const OUString
& getType() const { return maType
; }
340 void setHierarchyURL( const OUString
& rURL
) { maHierarchyURL
= rURL
; }
341 void setTargetURL( const OUString
& rURL
) { maTargetURL
= rURL
; }
342 void setType( const OUString
& rType
) { maType
= rType
; }
348 std::vector
< std::unique_ptr
<DocTemplates_EntryData_Impl
> > maEntries
;
350 OUString maHierarchyURL
;
351 OUString maTargetURL
;
353 bool mbInHierarchy
: 1;
356 explicit GroupData_Impl( const OUString
& rTitle
);
358 void setInUse() { mbInUse
= true; }
359 void setHierarchy( bool bInHierarchy
) { mbInHierarchy
= bInHierarchy
; }
360 void setHierarchyURL( const OUString
& rURL
) { maHierarchyURL
= rURL
; }
361 void setTargetURL( const OUString
& rURL
) { maTargetURL
= rURL
; }
363 bool getInUse() const { return mbInUse
; }
364 bool getInHierarchy() const { return mbInHierarchy
; }
365 const OUString
& getHierarchyURL() const { return maHierarchyURL
; }
366 const OUString
& getTargetURL() const { return maTargetURL
; }
367 const OUString
& getTitle() const { return maTitle
; }
369 DocTemplates_EntryData_Impl
* addEntry( const OUString
& rTitle
,
370 const OUString
& rTargetURL
,
371 const OUString
& rType
,
372 const OUString
& rHierURL
);
373 size_t count() { return maEntries
.size(); }
374 DocTemplates_EntryData_Impl
* getEntry( size_t nPos
) { return maEntries
[ nPos
].get(); }
378 // private SfxDocTplService_Impl
380 void SfxDocTplService_Impl::init_Impl()
382 uno::Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
383 uno::Reference
< task::XInteractionHandler
> xInteractionHandler(
384 task::InteractionHandler::createWithParent(xContext
, nullptr), uno::UNO_QUERY_THROW
);
385 maCmdEnv
= new TplTaskEnvironment( xInteractionHandler
);
387 ::osl::ClearableMutexGuard
aGuard( maMutex
);
388 bool bIsInitialized
= false;
389 bool bNeedsUpdate
= false;
394 // convert locale to string
395 // set maRootContent to the root of the templates hierarchy. Create the
396 // entry if necessary
398 maRootURL
= ( TEMPLATE_ROOT_URL
"/" ) + LanguageTag::convertToBcp47(maLocale
);
400 const OUString
aTemplVersPropName( TEMPLATE_VERSION
);
401 const OUString
aTemplVers( TEMPLATE_VERSION_VALUE
);
402 if ( Content::create( maRootURL
, maCmdEnv
, comphelper::getProcessComponentContext(), maRootContent
) )
406 if ( getProperty( maRootContent
, aTemplVersPropName
, aValue
)
407 && ( aValue
>>= aPropValue
)
408 && aPropValue
== aTemplVers
)
410 bIsInitialized
= true;
413 removeContent( maRootContent
);
416 if ( !bIsInitialized
)
418 if ( createFolder( maRootURL
, true, false, maRootContent
)
419 && setProperty( maRootContent
, aTemplVersPropName
, uno::makeAny( aTemplVers
) ) )
420 bIsInitialized
= true;
425 if ( bIsInitialized
)
428 m_xDocProps
.set(document::DocumentProperties::create(
429 ::comphelper::getProcessComponentContext()));
430 } catch (uno::RuntimeException
const&) {
431 TOOLS_WARN_EXCEPTION("sfx.doc", "SfxDocTplService_Impl::init_Impl: cannot create DocumentProperties service:");
434 mxType
.set( mxContext
->getServiceManager()->createInstanceWithContext(SERVICENAME_TYPEDETECTION
, mxContext
), UNO_QUERY
);
442 SolarMutexClearableGuard aSolarGuard
;
444 VclPtrInstance
< WaitWindow_Impl
> pWin
;
447 osl::MutexGuard
anotherGuard(maMutex
);
450 SolarMutexGuard aSecondSolarGuard
;
452 pWin
.disposeAndClear();
454 else if ( needsUpdate() )
455 // the UI should be shown only on the first update
460 SAL_WARN( "sfx.doc", "init_Impl(): Could not create root" );
463 mbIsInitialized
= bIsInitialized
;
467 void SfxDocTplService_Impl::getDefaultLocale()
471 ::osl::MutexGuard
aGuard( maMutex
);
474 maLocale
= LanguageTag::convertToLocale( utl::ConfigManager::getUILocale(), false);
480 const char* TEMPLATE_SHORT_NAMES_ARY
[] =
496 void SfxDocTplService_Impl::readFolderList()
498 SolarMutexGuard aGuard
;
500 static_assert( SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY
) == SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY
), "mismatch array lengths" );
501 const size_t nCount
= std::min(SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY
), SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY
));
502 for (size_t i
= 0; i
< nCount
; ++i
)
504 std::unique_ptr
<NamePair_Impl
> pPair( new NamePair_Impl
);
505 pPair
->maShortName
= OUString::createFromAscii(TEMPLATE_SHORT_NAMES_ARY
[i
]);
506 pPair
->maLongName
= SfxResId(TEMPLATE_LONG_NAMES_ARY
[i
]);
508 maNames
.push_back( std::move(pPair
) );
513 OUString
SfxDocTplService_Impl::getLongName( const OUString
& rShortName
)
517 for (auto const & pPair
: maNames
)
519 if ( pPair
->maShortName
== rShortName
)
521 aRet
= pPair
->maLongName
;
526 if ( aRet
.isEmpty() )
533 void SfxDocTplService_Impl::getDirList()
537 // Get the template dir list
538 // TODO/LATER: let use service, register listener
540 OUString aDirs
= SvtPathOptions().GetTemplatePath();
541 sal_Int32 nCount
= comphelper::string::getTokenCount(aDirs
, C_DELIM
);
543 maTemplateDirs
= Sequence
< OUString
>( nCount
);
545 uno::Reference
< util::XMacroExpander
> xExpander
= util::theMacroExpander::get(mxContext
);
546 const OUString
aPrefix(
547 "vnd.sun.star.expand:" );
550 for (auto& rTemplateDir
: maTemplateDirs
)
552 aURL
.SetSmartProtocol( INetProtocol::File
);
553 aURL
.SetURL( aDirs
.getToken( 0, C_DELIM
, nIdx
) );
554 rTemplateDir
= aURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
556 if ( xExpander
.is() )
558 const sal_Int32 nIndex
{ rTemplateDir
.indexOf( aPrefix
) };
562 rTemplateDir
= rTemplateDir
.replaceAt(nIndex
, aPrefix
.getLength(), OUString());
563 rTemplateDir
= xExpander
->expandMacros( rTemplateDir
);
567 aValue
<<= maTemplateDirs
;
569 css::uno::Reference
< css::util::XPathSettings
> xPathSettings
=
570 css::util::thePathSettings::get(mxContext
);
572 // load internal paths
573 Any aAny
= xPathSettings
->getPropertyValue( "Template_internal" );
574 aAny
>>= maInternalTemplateDirs
;
576 for (auto& rInternalTemplateDir
: maInternalTemplateDirs
)
578 //expand vnd.sun.star.expand: and remove "..." from them
579 //to normalize into the expected url patterns
580 maRelocator
.makeRelocatableURL(rInternalTemplateDir
);
581 maRelocator
.makeAbsoluteURL(rInternalTemplateDir
);
584 // Store the template dir list
585 setProperty( maRootContent
, PROPERTY_DIRLIST
, aValue
);
589 bool SfxDocTplService_Impl::needsUpdate()
591 bool bNeedsUpdate
= true;
594 // Get the template dir list
595 bool bHasProperty
= getProperty( maRootContent
, PROPERTY_NEEDSUPDATE
, aValue
);
598 aValue
>>= bNeedsUpdate
;
600 // the old template component also checks this state, but it is initialized from this component
601 // so if this component was already updated the old component does not need such an update
602 ::svt::TemplateFolderCache aTempCache
;
604 bNeedsUpdate
= aTempCache
.needsUpdate();
607 aTempCache
.storeState();
613 bool SfxDocTplService_Impl::setTitleForURL( const OUString
& rURL
, const OUString
& aTitle
)
615 if (m_xDocProps
.is())
619 m_xDocProps
->loadFromMedium(rURL
, Sequence
<PropertyValue
>());
620 m_xDocProps
->setTitle(aTitle
);
622 uno::Reference
< embed::XStorage
> xStorage
= ::comphelper::OStorageHelper::GetStorageFromURL(
623 rURL
, embed::ElementModes::READWRITE
);
625 uno::Sequence
<beans::PropertyValue
> medium( comphelper::InitPropertySequence({
626 { "DocumentBaseURL", Any(rURL
) },
630 m_xDocProps
->storeToStorage(xStorage
, medium
);
641 void SfxDocTplService_Impl::getTitleFromURL( const OUString
& rURL
, OUString
& aTitle
, OUString
& aType
, bool& bDocHasTitle
)
643 bDocHasTitle
= false;
645 if (m_xDocProps
.is())
649 m_xDocProps
->loadFromMedium(rURL
, Sequence
<PropertyValue
>());
650 aTitle
= m_xDocProps
->getTitle();
657 if ( aType
.isEmpty() && mxType
.is() )
659 const OUString aDocType
{mxType
->queryTypeByURL( rURL
)};
660 if ( !aDocType
.isEmpty() )
663 uno::Reference
< container::XNameAccess
> xTypeDetection( mxType
, uno::UNO_QUERY_THROW
);
664 SequenceAsHashMap
aTypeProps( xTypeDetection
->getByName( aDocType
) );
665 aType
= aTypeProps
.getUnpackedValueOrDefault(
669 catch( uno::Exception
& )
673 if ( aTitle
.isEmpty() )
675 INetURLObject
aURL( rURL
);
677 aTitle
= aURL
.getName( INetURLObject::LAST_SEGMENT
, true,
678 INetURLObject::DecodeMechanism::WithCharset
);
685 bool SfxDocTplService_Impl::addEntry( Content
& rParentFolder
,
686 const OUString
& rTitle
,
687 const OUString
& rTargetURL
,
688 const OUString
& rType
)
690 bool bAddedEntry
= false;
692 INetURLObject
aLinkObj( rParentFolder
.getURL() );
693 aLinkObj
.insertName( rTitle
, false,
694 INetURLObject::LAST_SEGMENT
,
695 INetURLObject::EncodeMechanism::All
);
696 const OUString aLinkURL
{aLinkObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
700 if ( ! Content::create( aLinkURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aLink
) )
702 Sequence
< OUString
> aNames(3);
704 aNames
[1] = IS_FOLDER
;
705 aNames
[2] = TARGET_URL
;
707 Sequence
< Any
> aValues(3);
708 aValues
[0] <<= rTitle
;
709 aValues
[1] <<= false;
710 aValues
[2] <<= rTargetURL
;
714 rParentFolder
.insertNewContent( TYPE_LINK
, aNames
, aValues
, aLink
);
715 setProperty( aLink
, PROPERTY_TYPE
, makeAny( rType
) );
725 bool SfxDocTplService_Impl::createFolder( const OUString
& rNewFolderURL
,
728 Content
&rNewFolder
)
731 bool bCreatedFolder
= false;
732 INetURLObject
aParentURL( rNewFolderURL
);
733 const OUString aFolderName
{aParentURL
.getName( INetURLObject::LAST_SEGMENT
, true,
734 INetURLObject::DecodeMechanism::WithCharset
)};
736 // compute the parent folder url from the new folder url
737 // and remove the final slash, because Content::create doesn't
739 aParentURL
.removeSegment();
740 if ( aParentURL
.getSegmentCount() >= 1 )
741 aParentURL
.removeFinalSlash();
743 // if the parent exists, we can continue with the creation of the
744 // new folder, we have to create the parent otherwise ( as long as
745 // bCreateParent is set to true )
746 if ( Content::create( aParentURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), maCmdEnv
, comphelper::getProcessComponentContext(), aParent
) )
750 Sequence
< OUString
> aNames(2);
752 aNames
[1] = IS_FOLDER
;
754 Sequence
< Any
> aValues(2);
755 aValues
[0] <<= aFolderName
;
761 aType
= TYPE_FSYS_FOLDER
;
765 aParent
.insertNewContent( aType
, aNames
, aValues
, rNewFolder
);
766 bCreatedFolder
= true;
768 catch( Exception
const & )
770 TOOLS_WARN_EXCEPTION( "sfx.doc", "createFolder(): Could not create new folder" );
773 else if ( bCreateParent
)
775 // if the parent doesn't exists and bCreateParent is set to true,
776 // we try to create the parent and if this was successful, we
777 // try to create the new folder again ( but this time, we set
778 // bCreateParent to false to avoid endless recursions )
779 if ( ( aParentURL
.getSegmentCount() >= 1 ) &&
780 createFolder( aParentURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), bCreateParent
, bFsysFolder
, aParent
) )
782 bCreatedFolder
= createFolder( rNewFolderURL
, false, bFsysFolder
, rNewFolder
);
786 return bCreatedFolder
;
790 bool SfxDocTplService_Impl::CreateNewUniqueFolderWithPrefix( const OUString
& aPath
,
791 const OUString
& aPrefix
,
792 OUString
& aNewFolderName
,
793 OUString
& aNewFolderURL
,
794 Content
& aNewFolder
)
796 bool bCreated
= false;
797 INetURLObject
aDirPath( aPath
);
800 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
801 if ( Content::create( aDirPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), aQuietEnv
, comphelper::getProcessComponentContext(), aParent
) )
803 for ( sal_Int32 nInd
= 0; nInd
< 32000; nInd
++ )
805 OUString aTryName
= aPrefix
;
807 aTryName
+= OUString::number( nInd
);
811 Sequence
< OUString
> aNames(2);
813 aNames
[1] = IS_FOLDER
;
815 Sequence
< Any
> aValues(2);
816 aValues
[0] <<= aTryName
;
819 bCreated
= aParent
.insertNewContent( TYPE_FSYS_FOLDER
, aNames
, aValues
, aNewFolder
);
821 catch( ucb::NameClashException
& )
823 // if there is already an element, retry
827 INetURLObject
aObjPath( aDirPath
);
828 aObjPath
.insertName( aTryName
, false,
829 INetURLObject::LAST_SEGMENT
,
830 INetURLObject::EncodeMechanism::All
);
831 // if there is already an element, retry
832 // if there was another error, do not try any more
833 if ( !::utl::UCBContentHelper::Exists( aObjPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
839 aNewFolderName
= aTryName
;
840 aNewFolderURL
= aNewFolder
.get()->getIdentifier()->getContentIdentifier();
850 OUString
SfxDocTplService_Impl::CreateNewUniqueFileWithPrefix( const OUString
& aPath
,
851 const OUString
& aPrefix
,
852 const OUString
& aExt
)
854 OUString aNewFileURL
;
855 INetURLObject
aDirPath( aPath
);
859 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
860 if ( Content::create( aDirPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), aQuietEnv
, comphelper::getProcessComponentContext(), aParent
) )
862 for ( sal_Int32 nInd
= 0; nInd
< 32000; nInd
++ )
865 bool bCreated
= false;
866 OUString aTryName
= aPrefix
;
868 aTryName
+= OUString::number( nInd
);
869 if ( aExt
.toChar() != '.' )
875 Sequence
< OUString
> aNames(2);
877 aNames
[1] = IS_DOCUMENT
;
879 Sequence
< Any
> aValues(2);
880 aValues
[0] <<= aTryName
;
883 bCreated
= aParent
.insertNewContent( TYPE_FSYS_FILE
, aNames
, aValues
, aNewFile
);
885 catch( ucb::NameClashException
& )
887 // if there is already an element, retry
891 INetURLObject
aObjPath( aPath
);
892 aObjPath
.insertName( aTryName
, false,
893 INetURLObject::LAST_SEGMENT
,
894 INetURLObject::EncodeMechanism::All
);
895 // if there is already an element, retry
896 // if there was another error, do not try any more
897 if ( !::utl::UCBContentHelper::Exists( aObjPath
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) )
903 aNewFileURL
= aNewFile
.get()->getIdentifier()->getContentIdentifier();
913 bool SfxDocTplService_Impl::removeContent( Content
& rContent
)
915 bool bRemoved
= false;
918 Any aArg
= makeAny( true );
920 rContent
.executeCommand( COMMAND_DELETE
, aArg
);
923 catch ( RuntimeException
& ) {}
924 catch ( Exception
& ) {}
930 bool SfxDocTplService_Impl::removeContent( const OUString
& rContentURL
)
934 if ( Content::create( rContentURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aContent
) )
935 return removeContent( aContent
);
940 bool SfxDocTplService_Impl::setProperty( Content
& rContent
,
941 const OUString
& rPropName
,
942 const Any
& rPropValue
)
944 bool bPropertySet
= false;
946 // Store the property
949 Any
aPropValue( rPropValue
);
950 uno::Reference
< XPropertySetInfo
> aPropInfo
= rContent
.getProperties();
952 // check, whether or not the property exists, create it, when not
953 if ( !aPropInfo
.is() || !aPropInfo
->hasPropertyByName( rPropName
) )
955 uno::Reference
< XPropertyContainer
> xProperties( rContent
.get(), UNO_QUERY
);
956 if ( xProperties
.is() )
960 xProperties
->addProperty( rPropName
, PropertyAttribute::MAYBEVOID
, rPropValue
);
962 catch( PropertyExistException
& ) {}
963 catch( IllegalTypeException
& ) {
964 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
966 catch( IllegalArgumentException
& ) {
967 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
972 // To ensure a reloctable office installation, the path to the
973 // office installation directory must never be stored directly.
974 if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName
) )
977 if ( rPropValue
>>= aValue
)
979 maRelocator
.makeRelocatableURL( aValue
);
980 aPropValue
<<= aValue
;
984 Sequence
< OUString
> aValues
;
985 if ( rPropValue
>>= aValues
)
987 for ( auto& rValue
: aValues
)
989 maRelocator
.makeRelocatableURL( rValue
);
991 aPropValue
<<= aValues
;
995 OSL_FAIL( "Unsupported property value type" );
1000 // now set the property
1002 rContent
.setPropertyValue( rPropName
, aPropValue
);
1003 bPropertySet
= true;
1005 catch ( RuntimeException
& ) {}
1006 catch ( Exception
& ) {}
1008 return bPropertySet
;
1012 bool SfxDocTplService_Impl::getProperty(Content
& rContent
, const OUString
& rPropName
, Any
& rPropValue
)
1014 bool bGotProperty
= false;
1019 uno::Reference
< XPropertySetInfo
> aPropInfo
= rContent
.getProperties();
1021 // check, whether or not the property exists
1022 if ( !aPropInfo
.is() || !aPropInfo
->hasPropertyByName( rPropName
) )
1027 // now get the property
1029 rPropValue
= rContent
.getPropertyValue( rPropName
);
1031 // To ensure a reloctable office installation, the path to the
1032 // office installation directory must never be stored directly.
1033 if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName
) )
1036 if ( rPropValue
>>= aValue
)
1038 maRelocator
.makeAbsoluteURL( aValue
);
1039 rPropValue
<<= aValue
;
1043 Sequence
< OUString
> aValues
;
1044 if ( rPropValue
>>= aValues
)
1046 for ( auto& rValue
: aValues
)
1048 maRelocator
.makeAbsoluteURL( rValue
);
1050 rPropValue
<<= aValues
;
1054 OSL_FAIL( "Unsupported property value type" );
1059 bGotProperty
= true;
1061 catch ( RuntimeException
& ) {}
1062 catch ( Exception
& ) {}
1064 return bGotProperty
;
1067 SfxDocTplService_Impl::SfxDocTplService_Impl( const uno::Reference
< XComponentContext
> & xContext
)
1068 : mxContext(xContext
), mbIsInitialized(false), mbLocaleSet(false), maRelocator(xContext
)
1073 SfxDocTplService_Impl::~SfxDocTplService_Impl()
1075 ::osl::MutexGuard
aGuard( maMutex
);
1080 lang::Locale
SfxDocTplService_Impl::getLocale()
1082 ::osl::MutexGuard
aGuard( maMutex
);
1091 void SfxDocTplService_Impl::setLocale( const lang::Locale
&rLocale
)
1093 ::osl::MutexGuard
aGuard( maMutex
);
1095 if ( mbLocaleSet
&& (
1096 ( maLocale
.Language
!= rLocale
.Language
) ||
1097 ( maLocale
.Country
!= rLocale
.Country
) ||
1098 ( maLocale
.Variant
!= rLocale
.Variant
) ) )
1099 mbIsInitialized
= false;
1106 void SfxDocTplService_Impl::update()
1108 ::osl::MutexGuard
aGuard( maMutex
);
1114 void SfxDocTplService_Impl::doUpdate()
1116 ::osl::MutexGuard
aGuard( maMutex
);
1118 const OUString
aPropName( PROPERTY_NEEDSUPDATE
);
1122 setProperty( maRootContent
, aPropName
, aValue
);
1124 GroupList_Impl aGroupList
;
1126 // get the entries from the hierarchy
1127 createFromContent( aGroupList
, maRootContent
, true, false );
1129 // get the entries from the template directories
1130 sal_Int32 nCountDir
= maTemplateDirs
.getLength();
1131 OUString
* pDirs
= maTemplateDirs
.getArray();
1132 Content aDirContent
;
1134 // the last directory in the list must be writable
1135 bool bWriteableDirectory
= true;
1137 // the target folder might not exist, for this reason no interaction handler should be used
1138 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
1143 if ( Content::create( pDirs
[ nCountDir
], aQuietEnv
, comphelper::getProcessComponentContext(), aDirContent
) )
1145 createFromContent( aGroupList
, aDirContent
, false, bWriteableDirectory
);
1148 bWriteableDirectory
= false;
1151 // now check the list
1152 for(std::unique_ptr
<GroupData_Impl
>& pGroup
: aGroupList
)
1154 if ( pGroup
->getInUse() )
1156 if ( pGroup
->getInHierarchy() )
1159 if ( Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1160 setProperty( aGroup
,
1162 makeAny( pGroup
->getTargetURL() ) );
1164 size_t nCount
= pGroup
->count();
1165 for ( size_t i
=0; i
<nCount
; i
++ )
1167 DocTemplates_EntryData_Impl
*pData
= pGroup
->getEntry( i
);
1168 if ( ! pData
->getInUse() )
1170 if ( pData
->getInHierarchy() )
1171 removeFromHierarchy( pData
); // delete entry in hierarchy
1173 addToHierarchy( pGroup
.get(), pData
); // add entry to hierarchy
1175 else if ( pData
->getUpdateType() ||
1176 pData
->getUpdateLink() )
1178 updateData( pData
);
1184 addGroupToHierarchy( pGroup
.get() ); // add group to hierarchy
1188 removeFromHierarchy( pGroup
.get() ); // delete group from hierarchy
1193 setProperty( maRootContent
, aPropName
, aValue
);
1197 std::vector
< beans::StringPair
> SfxDocTplService_Impl::ReadUINamesForTemplateDir_Impl( const OUString
& aUserPath
)
1199 INetURLObject
aLocObj( aUserPath
);
1200 aLocObj
.insertName( "groupuinames.xml", false,
1201 INetURLObject::LAST_SEGMENT
,
1202 INetURLObject::EncodeMechanism::All
);
1203 Content aLocContent
;
1205 // TODO/LATER: Use hashmap in future
1206 std::vector
< beans::StringPair
> aUINames
;
1207 if ( Content::create( aLocObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), uno::Reference
< ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext(), aLocContent
) )
1211 uno::Reference
< io::XInputStream
> xLocStream
= aLocContent
.openStream();
1212 if ( xLocStream
.is() )
1213 aUINames
= DocTemplLocaleHelper::ReadGroupLocalizationSequence( xLocStream
, mxContext
);
1215 catch( uno::Exception
& )
1223 bool SfxDocTplService_Impl::UpdateUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
1224 const OUString
& aGroupName
,
1225 const OUString
& aNewFolderName
)
1227 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1228 sal_Int32 nLen
= aUINames
.size();
1230 // it is possible that the name is used already, but it should be checked before
1231 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1232 if ( aUINames
[nInd
].First
== aNewFolderName
)
1235 aUINames
.resize( ++nLen
);
1236 aUINames
[nLen
-1].First
= aNewFolderName
;
1237 aUINames
[nLen
-1].Second
= aGroupName
;
1239 return WriteUINamesForTemplateDir_Impl( aUserPath
, aUINames
);
1243 bool SfxDocTplService_Impl::ReplaceUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
1244 const OUString
& aDefaultFsysGroupName
,
1245 const OUString
& aOldGroupName
,
1246 const OUString
& aNewGroupName
)
1248 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1249 sal_Int32 nLen
= aUINames
.size();
1251 bool bChanged
= false;
1252 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1253 if ( aUINames
[nInd
].Second
== aOldGroupName
)
1255 aUINames
[nInd
].Second
= aNewGroupName
;
1261 aUINames
.resize( ++nLen
);
1262 aUINames
[nLen
-1].First
= aDefaultFsysGroupName
;
1263 aUINames
[nLen
-1].Second
= aNewGroupName
;
1265 return WriteUINamesForTemplateDir_Impl( aUserPath
, aUINames
);
1269 void SfxDocTplService_Impl::RemoveUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
1270 const OUString
& aGroupName
)
1272 std::vector
< beans::StringPair
> aUINames
= ReadUINamesForTemplateDir_Impl( aUserPath
);
1273 sal_Int32 nLen
= aUINames
.size();
1274 std::vector
< beans::StringPair
> aNewUINames( nLen
);
1275 sal_Int32 nNewLen
= 0;
1277 bool bChanged
= false;
1278 for ( sal_Int32 nInd
= 0; nInd
< nLen
; nInd
++ )
1279 if ( aUINames
[nInd
].Second
== aGroupName
)
1284 aNewUINames
[nNewLen
-1].First
= aUINames
[nInd
].First
;
1285 aNewUINames
[nNewLen
-1].Second
= aUINames
[nInd
].Second
;
1288 aNewUINames
.resize( nNewLen
);
1291 WriteUINamesForTemplateDir_Impl( aUserPath
, aNewUINames
);
1295 bool SfxDocTplService_Impl::WriteUINamesForTemplateDir_Impl( const OUString
& aUserPath
,
1296 const std::vector
< beans::StringPair
>& aUINames
)
1298 bool bResult
= false;
1300 uno::Reference
< beans::XPropertySet
> xTempFile(
1301 io::TempFile::create(mxContext
),
1302 uno::UNO_QUERY_THROW
);
1305 uno::Any aUrl
= xTempFile
->getPropertyValue("Uri");
1308 uno::Reference
< io::XStream
> xStream( xTempFile
, uno::UNO_QUERY_THROW
);
1309 uno::Reference
< io::XOutputStream
> xOutStream
= xStream
->getOutputStream();
1310 if ( !xOutStream
.is() )
1311 throw uno::RuntimeException();
1313 DocTemplLocaleHelper::WriteGroupLocalizationSequence( xOutStream
, aUINames
, mxContext
);
1315 // the SAX writer might close the stream
1316 xOutStream
->closeOutput();
1317 } catch( uno::Exception
& )
1320 Content
aTargetContent( aUserPath
, maCmdEnv
, comphelper::getProcessComponentContext() );
1321 Content
aSourceContent( aTempURL
, maCmdEnv
, comphelper::getProcessComponentContext() );
1322 aTargetContent
.transferContent( aSourceContent
,
1323 InsertOperation::Copy
,
1325 ucb::NameClash::OVERWRITE
,
1329 catch ( uno::Exception
& )
1337 OUString
SfxDocTplService_Impl::CreateNewGroupFsys( const OUString
& rGroupName
, Content
& aGroup
)
1339 OUString aResultURL
;
1341 if ( maTemplateDirs
.hasElements() )
1343 OUString aTargetPath
= maTemplateDirs
[ maTemplateDirs
.getLength() - 1 ];
1345 // create a new folder with the given name
1347 OUString aNewFolderName
;
1349 // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1350 if ( !CreateNewUniqueFolderWithPrefix( aTargetPath
,
1355 && !CreateNewUniqueFolderWithPrefix( aTargetPath
,
1363 if ( !UpdateUINamesForTemplateDir_Impl( aTargetPath
, rGroupName
, aNewFolderName
) )
1365 // we could not create the groupuinames for the folder, so we delete the group in
1366 // the folder and return
1367 removeContent( aNewFolder
);
1371 // Now set the target url for this group and we are done
1372 Any aValue
= makeAny( aResultURL
);
1374 if ( ! setProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1376 removeContent( aNewFolder
);
1385 bool SfxDocTplService_Impl::addGroup( const OUString
& rGroupName
)
1387 ::osl::MutexGuard
aGuard( maMutex
);
1389 // Check, whether or not there is a group with this name
1391 OUString aNewGroupURL
;
1392 INetURLObject
aNewGroupObj( maRootURL
);
1394 aNewGroupObj
.insertName( rGroupName
, false,
1395 INetURLObject::LAST_SEGMENT
,
1396 INetURLObject::EncodeMechanism::All
);
1398 aNewGroupURL
= aNewGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1400 if ( Content::create( aNewGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aNewGroup
) ||
1401 ! createFolder( aNewGroupURL
, false, false, aNewGroup
) )
1403 // if there already was a group with this name or the new group
1404 // could not be created, we return here
1408 // Get the user template path entry ( new group will always
1409 // be added in the user template path )
1413 nIndex
= maTemplateDirs
.getLength();
1417 return false; // We don't know where to add the group
1419 aUserPath
= maTemplateDirs
[ nIndex
];
1421 // create a new folder with the given name
1423 OUString aNewFolderName
;
1424 OUString aNewFolderURL
;
1426 // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1427 if ( !CreateNewUniqueFolderWithPrefix( aUserPath
,
1432 && !CreateNewUniqueFolderWithPrefix( aUserPath
,
1438 // we could not create the folder, so we delete the group in the
1439 // hierarchy and return
1440 removeContent( aNewGroup
);
1444 if ( !UpdateUINamesForTemplateDir_Impl( aUserPath
, rGroupName
, aNewFolderName
) )
1446 // we could not create the groupuinames for the folder, so we delete the group in the
1447 // hierarchy, the folder and return
1448 removeContent( aNewGroup
);
1449 removeContent( aNewFolder
);
1453 // Now set the target url for this group and we are done
1454 Any aValue
= makeAny( aNewFolderURL
);
1456 if ( ! setProperty( aNewGroup
, TARGET_DIR_URL
, aValue
) )
1458 removeContent( aNewGroup
);
1459 removeContent( aNewFolder
);
1467 bool SfxDocTplService_Impl::removeGroup( const OUString
& rGroupName
)
1469 // remove all the elements that have the prefix aTargetURL
1470 // if the group does not have other elements remove it
1472 ::osl::MutexGuard
aGuard( maMutex
);
1474 bool bResult
= false;
1476 // create the group url
1477 INetURLObject
aGroupObj( maRootURL
);
1478 aGroupObj
.insertName( rGroupName
, false,
1479 INetURLObject::LAST_SEGMENT
,
1480 INetURLObject::EncodeMechanism::All
);
1482 // Get the target url
1484 const OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1486 if ( Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1488 const OUString
aPropName( TARGET_DIR_URL
);
1491 OUString aGroupTargetURL
;
1492 if ( getProperty( aGroup
, aPropName
, aValue
) )
1493 aValue
>>= aGroupTargetURL
;
1495 if ( aGroupTargetURL
.isEmpty() )
1496 return false; // nothing is allowed to be removed
1498 if ( !maTemplateDirs
.hasElements() )
1501 // check that the fs location is in writable folder and this is not a "My templates" folder
1502 INetURLObject
aGroupParentFolder( aGroupTargetURL
);
1503 if (!aGroupParentFolder
.removeSegment())
1506 OUString aGeneralTempPath
= findParentTemplateDir(
1507 aGroupParentFolder
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1509 if (aGeneralTempPath
.isEmpty())
1512 // now get the content of the Group
1513 uno::Reference
< XResultSet
> xResultSet
;
1514 Sequence
< OUString
> aProps
{ TARGET_URL
};
1518 xResultSet
= aGroup
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
1520 if ( xResultSet
.is() )
1522 bool bHasNonRemovable
= false;
1523 bool bHasShared
= false;
1525 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY_THROW
);
1526 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY_THROW
);
1528 while ( xResultSet
->next() )
1530 OUString
aTemplTargetURL( xRow
->getString( 1 ) );
1531 OUString aHierURL
= xContentAccess
->queryContentIdentifierString();
1533 if ( ::utl::UCBContentHelper::IsSubPath( aGroupTargetURL
, aTemplTargetURL
) )
1535 // this is a user template, and it can be removed
1536 if ( removeContent( aTemplTargetURL
) )
1537 removeContent( aHierURL
);
1539 bHasNonRemovable
= true;
1545 if ( !bHasNonRemovable
&& !bHasShared
)
1547 if ( removeContent( aGroupTargetURL
)
1548 || !::utl::UCBContentHelper::Exists( aGroupTargetURL
) )
1550 removeContent( aGroupURL
);
1551 RemoveUINamesForTemplateDir_Impl( aGeneralTempPath
, rGroupName
);
1552 bResult
= true; // the operation is successful only if the whole group is removed
1555 else if ( !bHasNonRemovable
)
1557 if ( removeContent( aGroupTargetURL
)
1558 || !::utl::UCBContentHelper::Exists( aGroupTargetURL
) )
1560 RemoveUINamesForTemplateDir_Impl( aGeneralTempPath
, rGroupName
);
1561 setProperty( aGroup
, aPropName
, uno::makeAny( OUString() ) );
1566 catch ( Exception
& ) {}
1573 bool SfxDocTplService_Impl::renameGroup( const OUString
& rOldName
,
1574 const OUString
& rNewName
)
1576 ::osl::MutexGuard
aGuard( maMutex
);
1578 // create the group url
1580 INetURLObject
aGroupObj( maRootURL
);
1581 aGroupObj
.insertName( rNewName
, false,
1582 INetURLObject::LAST_SEGMENT
,
1583 INetURLObject::EncodeMechanism::All
);
1584 OUString aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1586 // Check, if there is a group with the new name, return false
1588 if ( Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1591 aGroupObj
.removeSegment();
1592 aGroupObj
.insertName( rOldName
, false,
1593 INetURLObject::LAST_SEGMENT
,
1594 INetURLObject::EncodeMechanism::All
);
1595 aGroupURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1597 // When there is no group with the old name, we can't rename it
1598 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1601 OUString aGroupTargetURL
;
1602 // there is no need to check whether target dir url is in target path, since if the target path is changed
1603 // the target dir url should be already generated new
1605 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1606 aValue
>>= aGroupTargetURL
;
1608 if ( aGroupTargetURL
.isEmpty() )
1611 if ( !maTemplateDirs
.hasElements() )
1614 // check that the fs location is in writable folder and this is not a "My templates" folder
1615 INetURLObject
aGroupParentFolder( aGroupTargetURL
);
1616 if (!aGroupParentFolder
.removeSegment() ||
1617 isInternalTemplateDir(aGroupParentFolder
.GetMainURL(INetURLObject::DecodeMechanism::NONE
)))
1622 // check that the group can be renamed ( all the contents must be in target location )
1623 bool bCanBeRenamed
= false;
1626 uno::Reference
< XResultSet
> xResultSet
;
1627 Sequence
< OUString
> aProps
{ TARGET_URL
};
1628 xResultSet
= aGroup
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
1630 if ( xResultSet
.is() )
1632 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY_THROW
);
1633 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY_THROW
);
1635 while ( xResultSet
->next() )
1637 if ( !::utl::UCBContentHelper::IsSubPath( aGroupTargetURL
, xRow
->getString( 1 ) ) )
1638 throw uno::Exception("not sub path", nullptr);
1641 bCanBeRenamed
= true;
1644 catch ( Exception
& ) {}
1646 if ( bCanBeRenamed
)
1648 INetURLObject
aGroupTargetObj( aGroupTargetURL
);
1649 const OUString aFsysName
= aGroupTargetObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
1651 if ( aGroupTargetObj
.removeSegment()
1652 && ReplaceUINamesForTemplateDir_Impl( aGroupTargetObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
1657 // rename the group in the hierarchy
1659 aTitleValue
<<= rNewName
;
1661 return setProperty( aGroup
, TITLE
, aTitleValue
);
1669 bool SfxDocTplService_Impl::storeTemplate( const OUString
& rGroupName
,
1670 const OUString
& rTemplateName
,
1671 const uno::Reference
< frame::XStorable
>& rStorable
)
1673 ::osl::MutexGuard
aGuard( maMutex
);
1675 // Check, whether or not there is a group with this name
1676 // Return false, if there is no group with the given name
1677 Content aGroup
, aTemplateToRemove
;
1678 INetURLObject
aGroupObj( maRootURL
);
1679 bool bRemoveOldTemplateContent
= false;
1681 aGroupObj
.insertName( rGroupName
, false,
1682 INetURLObject::LAST_SEGMENT
,
1683 INetURLObject::EncodeMechanism::All
);
1684 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1686 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
1689 OUString aGroupTargetURL
;
1691 if ( getProperty( aGroup
, TARGET_DIR_URL
, aValue
) )
1692 aValue
>>= aGroupTargetURL
;
1695 // Check, if there's a template with the given name in this group
1696 // the target template should be overwritten if it is imported by user
1697 // in case the template is installed by office installation of by an add-in
1698 // it can not be replaced
1699 aGroupObj
.insertName( rTemplateName
, false,
1700 INetURLObject::LAST_SEGMENT
,
1701 INetURLObject::EncodeMechanism::All
);
1702 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
1704 OUString aTemplateToRemoveTargetURL
;
1706 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplateToRemove
) )
1708 bRemoveOldTemplateContent
= true;
1709 if ( getProperty( aTemplateToRemove
, TARGET_URL
, aValue
) )
1710 aValue
>>= aTemplateToRemoveTargetURL
;
1712 if ( aGroupTargetURL
.isEmpty() || !maTemplateDirs
.hasElements()
1713 || (!aTemplateToRemoveTargetURL
.isEmpty() && isInternalTemplateDir(aTemplateToRemoveTargetURL
)) )
1714 return false; // it is not allowed to remove the template
1719 uno::Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
1721 // get document service name
1722 uno::Reference
< frame::XModuleManager2
> xModuleManager( frame::ModuleManager::create(xContext
) );
1723 const OUString sDocServiceName
{xModuleManager
->identify( uno::Reference
< uno::XInterface
>( rStorable
, uno::UNO_QUERY
) )};
1724 if ( sDocServiceName
.isEmpty() )
1725 throw uno::RuntimeException();
1727 // get the actual filter name
1728 uno::Reference
< lang::XMultiServiceFactory
> xConfigProvider
=
1729 configuration::theDefaultProvider::get( xContext
);
1731 uno::Sequence
<uno::Any
> aArgs(comphelper::InitAnyPropertySequence(
1733 {"nodepath", uno::Any(OUString( "/org.openoffice.Setup/Office/Factories/" ))}
1735 uno::Reference
< container::XNameAccess
> xSOFConfig(
1736 xConfigProvider
->createInstanceWithArguments(
1737 "com.sun.star.configuration.ConfigurationAccess",
1739 uno::UNO_QUERY_THROW
);
1741 uno::Reference
< container::XNameAccess
> xApplConfig
;
1742 xSOFConfig
->getByName( sDocServiceName
) >>= xApplConfig
;
1743 if ( !xApplConfig
.is() )
1744 throw uno::RuntimeException();
1746 OUString aFilterName
;
1747 xApplConfig
->getByName("ooSetupFactoryActualTemplateFilter") >>= aFilterName
;
1748 if ( aFilterName
.isEmpty() )
1749 throw uno::RuntimeException();
1751 // find the related type name
1752 uno::Reference
< container::XNameAccess
> xFilterFactory(
1753 mxContext
->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", mxContext
),
1754 uno::UNO_QUERY_THROW
);
1756 uno::Sequence
< beans::PropertyValue
> aFilterData
;
1757 xFilterFactory
->getByName( aFilterName
) >>= aFilterData
;
1759 for ( const auto& rProp
: std::as_const(aFilterData
) )
1760 if ( rProp
.Name
== "Type" )
1761 rProp
.Value
>>= aTypeName
;
1763 if ( aTypeName
.isEmpty() )
1764 throw uno::RuntimeException();
1766 // find the mediatype and extension
1767 uno::Reference
< container::XNameAccess
> xTypeDetection
=
1769 uno::Reference
< container::XNameAccess
>( mxType
, uno::UNO_QUERY_THROW
) :
1770 uno::Reference
< container::XNameAccess
>(
1771 mxContext
->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", mxContext
),
1772 uno::UNO_QUERY_THROW
);
1774 SequenceAsHashMap
aTypeProps( xTypeDetection
->getByName( aTypeName
) );
1775 uno::Sequence
< OUString
> aAllExt
=
1776 aTypeProps
.getUnpackedValueOrDefault("Extensions", Sequence
< OUString
>() );
1777 if ( !aAllExt
.hasElements() )
1778 throw uno::RuntimeException();
1780 const OUString aMediaType
{aTypeProps
.getUnpackedValueOrDefault("MediaType", OUString() )};
1781 const OUString aExt
{aAllExt
[0]};
1783 if ( aMediaType
.isEmpty() || aExt
.isEmpty() )
1784 throw uno::RuntimeException();
1786 // construct destination url
1787 if ( aGroupTargetURL
.isEmpty() )
1789 aGroupTargetURL
= CreateNewGroupFsys( rGroupName
, aGroup
);
1791 if ( aGroupTargetURL
.isEmpty() )
1792 throw uno::RuntimeException();
1795 OUString aNewTemplateTargetURL
= CreateNewUniqueFileWithPrefix( aGroupTargetURL
, rTemplateName
, aExt
);
1796 if ( aNewTemplateTargetURL
.isEmpty() )
1798 aNewTemplateTargetURL
= CreateNewUniqueFileWithPrefix( aGroupTargetURL
, "UserTemplate", aExt
);
1800 if ( aNewTemplateTargetURL
.isEmpty() )
1801 throw uno::RuntimeException();
1805 uno::Sequence
< PropertyValue
> aStoreArgs( 2 );
1806 aStoreArgs
[0].Name
= "FilterName";
1807 aStoreArgs
[0].Value
<<= aFilterName
;
1808 aStoreArgs
[1].Name
= "DocumentTitle";
1809 aStoreArgs
[1].Value
<<= rTemplateName
;
1811 if( !::utl::UCBContentHelper::EqualURLs( aNewTemplateTargetURL
, rStorable
->getLocation() ))
1812 rStorable
->storeToURL( aNewTemplateTargetURL
, aStoreArgs
);
1816 // the storing was successful, now the old template with the same name can be removed if it existed
1817 if ( !aTemplateToRemoveTargetURL
.isEmpty() )
1819 removeContent( aTemplateToRemoveTargetURL
);
1823 * if the old template was the standard template
1824 * it is necessary to change the standard template with the new file name
1826 const OUString sStdTmplFile
= SfxObjectFactory::GetStandardTemplate( sDocServiceName
);
1827 if ( INetURLObject( sStdTmplFile
) == INetURLObject( aTemplateToRemoveTargetURL
) )
1829 SfxObjectFactory::SetStandardTemplate( sDocServiceName
, aNewTemplateTargetURL
);
1833 if ( bRemoveOldTemplateContent
)
1834 removeContent( aTemplateToRemove
);
1836 // add the template to hierarchy
1837 return addEntry( aGroup
, rTemplateName
, aNewTemplateTargetURL
, aMediaType
);
1841 // the template was not stored
1847 bool SfxDocTplService_Impl::addTemplate( const OUString
& rGroupName
,
1848 const OUString
& rTemplateName
,
1849 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 const OUString
aPropertyName( "IsReadOnly" );
1955 bool bReadOnly
= false;
1956 if ( getProperty( aResultContent
, aPropertyName
, aProperty
) && ( aProperty
>>= bReadOnly
) && bReadOnly
)
1957 setProperty( aResultContent
, aPropertyName
, uno::makeAny( 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_Impl::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_Impl::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 bool SfxDocTplService_Impl::removeTemplate( const OUString
& rGroupName
,
2009 const OUString
& rTemplateName
)
2011 ::osl::MutexGuard
aGuard( maMutex
);
2013 // Check, whether or not there is a group with this name
2014 // Return false, if there is no group with the given name
2015 Content aGroup
, aTemplate
;
2016 INetURLObject
aGroupObj( maRootURL
);
2018 aGroupObj
.insertName( rGroupName
, false,
2019 INetURLObject::LAST_SEGMENT
,
2020 INetURLObject::EncodeMechanism::All
);
2021 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2023 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2026 // Check, if there's a template with the given name in this group
2027 // Return false, if there is no template
2028 aGroupObj
.insertName( rTemplateName
, false,
2029 INetURLObject::LAST_SEGMENT
,
2030 INetURLObject::EncodeMechanism::All
);
2031 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2033 if ( !Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2036 // get the target URL from the template
2037 OUString aTargetURL
;
2040 if ( getProperty( aTemplate
, TARGET_URL
, aValue
) )
2041 aValue
>>= aTargetURL
;
2043 // delete the target template
2044 if ( !aTargetURL
.isEmpty() )
2046 if (isInternalTemplateDir(aTargetURL
))
2049 removeContent( aTargetURL
);
2052 // delete the template entry
2053 return removeContent( aTemplate
);
2057 bool SfxDocTplService_Impl::renameTemplate( const OUString
& rGroupName
,
2058 const OUString
& rOldName
,
2059 const OUString
& rNewName
)
2061 ::osl::MutexGuard
aGuard( maMutex
);
2063 // Check, whether or not there is a group with this name
2064 // Return false, if there is no group with the given name
2065 Content aGroup
, aTemplate
;
2066 INetURLObject
aGroupObj( maRootURL
);
2068 aGroupObj
.insertName( rGroupName
, false,
2069 INetURLObject::LAST_SEGMENT
,
2070 INetURLObject::EncodeMechanism::All
);
2071 const OUString aGroupURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2073 if ( ! Content::create( aGroupURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2076 // Check, if there's a template with the new name in this group
2077 // Return false, if there is one
2078 aGroupObj
.insertName( rNewName
, false,
2079 INetURLObject::LAST_SEGMENT
,
2080 INetURLObject::EncodeMechanism::All
);
2081 OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2083 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2086 // Check, if there's a template with the old name in this group
2087 // Return false, if there is no template
2088 aGroupObj
.removeSegment();
2089 aGroupObj
.insertName( rOldName
, false,
2090 INetURLObject::LAST_SEGMENT
,
2091 INetURLObject::EncodeMechanism::All
);
2092 aTemplateURL
= aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2094 if ( !Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2097 OUString aTemplateTargetURL
;
2100 if ( getProperty( aTemplate
, TARGET_URL
, aTargetValue
) )
2101 aTargetValue
>>= aTemplateTargetURL
;
2103 if ( !setTitleForURL( aTemplateTargetURL
, rNewName
) )
2106 // rename the template entry in the cache
2108 aTitleValue
<<= rNewName
;
2110 return setProperty( aTemplate
, TITLE
, aTitleValue
);
2114 class SfxDocTplService
: public ::cppu::WeakImplHelper
< css::lang::XLocalizable
, css::frame::XDocumentTemplates
, css::lang::XServiceInfo
>
2116 std::unique_ptr
<SfxDocTplService_Impl
> pImp
;
2119 explicit SfxDocTplService( const css::uno::Reference
< uno::XComponentContext
>& xContext
);
2121 virtual OUString SAL_CALL
getImplementationName() override
2123 return "com.sun.star.comp.sfx2.DocumentTemplates";
2126 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
) override
2128 return cppu::supportsService(this, ServiceName
);
2131 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
2133 css::uno::Sequence
< OUString
> aSeq
{ "com.sun.star.frame.DocumentTemplates" };
2138 // --- XLocalizable ---
2139 void SAL_CALL
setLocale( const css::lang::Locale
& eLocale
) override
;
2140 css::lang::Locale SAL_CALL
getLocale() override
;
2142 // --- XDocumentTemplates ---
2143 css::uno::Reference
< css::ucb::XContent
> SAL_CALL
getContent() override
;
2144 sal_Bool SAL_CALL
storeTemplate( const OUString
& GroupName
,
2145 const OUString
& TemplateName
,
2146 const css::uno::Reference
< css::frame::XStorable
>& Storable
) override
;
2147 sal_Bool SAL_CALL
addTemplate( const OUString
& GroupName
,
2148 const OUString
& TemplateName
,
2149 const OUString
& SourceURL
) override
;
2150 sal_Bool SAL_CALL
removeTemplate( const OUString
& GroupName
,
2151 const OUString
& TemplateName
) override
;
2152 sal_Bool SAL_CALL
renameTemplate( const OUString
& GroupName
,
2153 const OUString
& OldTemplateName
,
2154 const OUString
& NewTemplateName
) override
;
2155 sal_Bool SAL_CALL
addGroup( const OUString
& GroupName
) override
;
2156 sal_Bool SAL_CALL
removeGroup( const OUString
& GroupName
) override
;
2157 sal_Bool SAL_CALL
renameGroup( const OUString
& OldGroupName
,
2158 const OUString
& NewGroupName
) override
;
2159 void SAL_CALL
update() override
;
2163 SfxDocTplService::SfxDocTplService( const uno::Reference
< XComponentContext
>& xContext
)
2165 pImp
.reset( new SfxDocTplService_Impl(xContext
) );
2170 //--- XLocalizable ---
2173 lang::Locale SAL_CALL
SfxDocTplService::getLocale()
2175 return pImp
->getLocale();
2179 void SAL_CALL
SfxDocTplService::setLocale( const lang::Locale
& rLocale
)
2181 pImp
->setLocale( rLocale
);
2185 //--- XDocumentTemplates ---
2187 uno::Reference
< ucb::XContent
> SAL_CALL
SfxDocTplService::getContent()
2190 return pImp
->getContent().get();
2195 sal_Bool SAL_CALL
SfxDocTplService::storeTemplate( const OUString
& GroupName
,
2196 const OUString
& TemplateName
,
2197 const uno::Reference
< frame::XStorable
>& Storable
)
2199 return pImp
->init() && pImp
->storeTemplate( GroupName
, TemplateName
, Storable
);
2203 sal_Bool SAL_CALL
SfxDocTplService::addTemplate( const OUString
& rGroupName
,
2204 const OUString
& rTemplateName
,
2205 const OUString
& rSourceURL
)
2207 return pImp
->init() && pImp
->addTemplate( rGroupName
, rTemplateName
, rSourceURL
);
2211 sal_Bool SAL_CALL
SfxDocTplService::removeTemplate( const OUString
& rGroupName
,
2212 const OUString
& rTemplateName
)
2214 return pImp
->init() && pImp
->removeTemplate( rGroupName
, rTemplateName
);
2218 sal_Bool SAL_CALL
SfxDocTplService::renameTemplate( const OUString
& rGroupName
,
2219 const OUString
& rOldName
,
2220 const OUString
& rNewName
)
2222 if ( rOldName
== rNewName
)
2225 return pImp
->init() && pImp
->renameTemplate( rGroupName
, rOldName
, rNewName
);
2229 sal_Bool SAL_CALL
SfxDocTplService::addGroup( const OUString
& rGroupName
)
2231 return pImp
->init() && pImp
->addGroup( rGroupName
);
2235 sal_Bool SAL_CALL
SfxDocTplService::removeGroup( const OUString
& rGroupName
)
2237 return pImp
->init() && pImp
->removeGroup( rGroupName
);
2241 sal_Bool SAL_CALL
SfxDocTplService::renameGroup( const OUString
& rOldName
,
2242 const OUString
& rNewName
)
2244 if ( rOldName
== rNewName
)
2247 return pImp
->init() && pImp
->renameGroup( rOldName
, rNewName
);
2251 void SAL_CALL
SfxDocTplService::update()
2257 WaitWindow_Impl::WaitWindow_Impl() : WorkWindow(nullptr, WB_BORDER
| WB_3DLOOK
)
2259 tools::Rectangle
aRect(0, 0, 300, 30000);
2260 maText
= SfxResId(RID_CNT_STR_WAITING
);
2261 maRect
= GetTextRect(aRect
, maText
, gnTextStyle
);
2263 aRect
.AdjustRight(2 * X_OFFSET
);
2264 aRect
.AdjustBottom(2 * Y_OFFSET
);
2265 maRect
.SetPos(Point(X_OFFSET
, Y_OFFSET
));
2266 SetOutputSizePixel(aRect
.GetSize());
2274 WaitWindow_Impl::~WaitWindow_Impl()
2279 void WaitWindow_Impl::dispose()
2282 WorkWindow::dispose();
2286 void WaitWindow_Impl::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*rRect*/)
2288 rRenderContext
.DrawText(maRect
, maText
, gnTextStyle
);
2291 void SfxDocTplService_Impl::addHierGroup( GroupList_Impl
& rList
,
2292 const OUString
& rTitle
,
2293 const OUString
& rOwnURL
)
2295 // now get the content of the Group
2297 uno::Reference
<XResultSet
> xResultSet
;
2298 Sequence
<OUString
> aProps(3);
2301 aProps
[1] = TARGET_URL
;
2302 aProps
[2] = PROPERTY_TYPE
;
2306 aContent
= Content(rOwnURL
, maCmdEnv
, comphelper::getProcessComponentContext());
2307 xResultSet
= aContent
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
2309 catch (ContentCreationException
&)
2311 TOOLS_WARN_EXCEPTION( "sfx.doc", "" );
2313 catch (Exception
&) {}
2315 if ( !xResultSet
.is() )
2318 GroupData_Impl
*pGroup
= new GroupData_Impl( rTitle
);
2319 pGroup
->setHierarchy( true );
2320 pGroup
->setHierarchyURL( rOwnURL
);
2321 rList
.push_back( std::unique_ptr
<GroupData_Impl
>(pGroup
) );
2323 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2324 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2328 while ( xResultSet
->next() )
2330 bool bUpdateType
= false;
2331 DocTemplates_EntryData_Impl
*pData
;
2333 const OUString
aTitle( xRow
->getString( 1 ) );
2334 const OUString
aTargetDir( xRow
->getString( 2 ) );
2335 OUString
aType( xRow
->getString( 3 ) );
2336 const OUString aHierURL
{xContentAccess
->queryContentIdentifierString()};
2338 if ( aType
.isEmpty() )
2342 bool bDocHasTitle
= false;
2343 getTitleFromURL( aTargetDir
, aTmpTitle
, aType
, bDocHasTitle
);
2345 if ( !aType
.isEmpty() )
2349 pData
= pGroup
->addEntry( aTitle
, aTargetDir
, aType
, aHierURL
);
2350 pData
->setUpdateType( bUpdateType
);
2353 catch ( Exception
& ) {}
2357 void SfxDocTplService_Impl::addFsysGroup( GroupList_Impl
& rList
,
2358 const OUString
& rTitle
,
2359 const OUString
& rUITitle
,
2360 const OUString
& rOwnURL
,
2361 bool bWriteableGroup
)
2365 if ( rUITitle
.isEmpty() )
2367 // reserved FS names that should not be used
2368 if ( rTitle
== "wizard" )
2370 else if ( rTitle
== "internal" )
2373 aTitle
= getLongName( rTitle
);
2378 if ( aTitle
.isEmpty() )
2381 GroupData_Impl
* pGroup
= nullptr;
2382 for (const std::unique_ptr
<GroupData_Impl
>& i
: rList
)
2384 if ( i
->getTitle() == aTitle
)
2393 pGroup
= new GroupData_Impl( aTitle
);
2394 rList
.push_back( std::unique_ptr
<GroupData_Impl
>(pGroup
) );
2397 if ( bWriteableGroup
)
2398 pGroup
->setTargetURL( rOwnURL
);
2402 // now get the content of the Group
2404 uno::Reference
< XResultSet
> xResultSet
;
2405 Sequence
< OUString
> aProps
{ TITLE
};
2409 // this method is only used during checking of the available template-folders
2410 // that should happen quietly
2411 uno::Reference
< XCommandEnvironment
> aQuietEnv
;
2412 aContent
= Content( rOwnURL
, aQuietEnv
, comphelper::getProcessComponentContext() );
2413 xResultSet
= aContent
.createCursor( aProps
, INCLUDE_DOCUMENTS_ONLY
);
2415 catch ( Exception
& ) {}
2417 if ( !xResultSet
.is() )
2420 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2421 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2425 while ( xResultSet
->next() )
2427 OUString
aChildTitle( xRow
->getString( 1 ) );
2428 const OUString aTargetURL
{xContentAccess
->queryContentIdentifierString()};
2431 if ( aChildTitle
== "sfx.tlx" || aChildTitle
== "groupuinames.xml" )
2434 bool bDocHasTitle
= false;
2435 getTitleFromURL( aTargetURL
, aChildTitle
, aType
, bDocHasTitle
);
2437 pGroup
->addEntry( aChildTitle
, aTargetURL
, aType
, OUString() );
2440 catch ( Exception
& ) {}
2444 void SfxDocTplService_Impl::createFromContent( GroupList_Impl
& rList
,
2447 bool bWriteableContent
)
2449 const OUString aTargetURL
{rContent
.get()->getIdentifier()->getContentIdentifier()};
2451 // when scanning the file system, we have to add the 'standard' group, too
2454 const OUString aUIStdTitle
{getLongName( STANDARD_FOLDER
)};
2455 addFsysGroup( rList
, OUString(), aUIStdTitle
, aTargetURL
, bWriteableContent
);
2458 // search for predefined UI names
2459 INetURLObject
aLayerObj( aTargetURL
);
2461 // TODO/LATER: Use hashmap in future
2462 std::vector
< beans::StringPair
> aUINames
;
2464 aUINames
= ReadUINamesForTemplateDir_Impl( aLayerObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
) );
2466 uno::Reference
< XResultSet
> xResultSet
;
2467 Sequence
< OUString
> aProps
{ TITLE
};
2471 xResultSet
= rContent
.createCursor( aProps
, INCLUDE_FOLDERS_ONLY
);
2473 catch ( Exception
& ) {}
2475 if ( !xResultSet
.is() )
2478 uno::Reference
< XContentAccess
> xContentAccess( xResultSet
, UNO_QUERY
);
2479 uno::Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
2483 while ( xResultSet
->next() )
2485 // TODO/LATER: clarify the encoding of the Title
2486 const OUString
aTitle( xRow
->getString( 1 ) );
2487 const OUString
aTargetSubfolderURL( xContentAccess
->queryContentIdentifierString() );
2490 addHierGroup( rList
, aTitle
, aTargetSubfolderURL
);
2494 for (const beans::StringPair
& rUIName
: aUINames
)
2495 if ( rUIName
.First
== aTitle
)
2497 aUITitle
= rUIName
.Second
;
2501 addFsysGroup( rList
, aTitle
, aUITitle
, aTargetSubfolderURL
, bWriteableContent
);
2505 catch ( Exception
& ) {}
2509 void SfxDocTplService_Impl::removeFromHierarchy( DocTemplates_EntryData_Impl
const *pData
)
2513 if ( Content::create( pData
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2515 removeContent( aTemplate
);
2520 void SfxDocTplService_Impl::addToHierarchy( GroupData_Impl
const *pGroup
,
2521 DocTemplates_EntryData_Impl
const *pData
)
2523 Content aGroup
, aTemplate
;
2525 if ( ! Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2528 // Check, if there's a template with the given name in this group
2529 // Return if there is already a template
2530 INetURLObject
aGroupObj( pGroup
->getHierarchyURL() );
2532 aGroupObj
.insertName( pData
->getTitle(), false,
2533 INetURLObject::LAST_SEGMENT
,
2534 INetURLObject::EncodeMechanism::All
);
2536 const OUString aTemplateURL
{aGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2538 if ( Content::create( aTemplateURL
, maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2541 addEntry( aGroup
, pData
->getTitle(),
2542 pData
->getTargetURL(),
2547 void SfxDocTplService_Impl::updateData( DocTemplates_EntryData_Impl
const *pData
)
2551 if ( ! Content::create( pData
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aTemplate
) )
2554 if ( pData
->getUpdateType() )
2556 setProperty( aTemplate
, PROPERTY_TYPE
, makeAny( pData
->getType() ) );
2559 if ( pData
->getUpdateLink() )
2561 setProperty( aTemplate
, TARGET_URL
, makeAny( pData
->getTargetURL() ) );
2566 void SfxDocTplService_Impl::addGroupToHierarchy( GroupData_Impl
*pGroup
)
2570 INetURLObject
aNewGroupObj( maRootURL
);
2571 aNewGroupObj
.insertName( pGroup
->getTitle(), false,
2572 INetURLObject::LAST_SEGMENT
,
2573 INetURLObject::EncodeMechanism::All
);
2575 const OUString aNewGroupURL
{aNewGroupObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
)};
2577 if ( createFolder( aNewGroupURL
, false, false, aGroup
) )
2579 setProperty( aGroup
, TARGET_DIR_URL
, makeAny( pGroup
->getTargetURL() ) );
2580 pGroup
->setHierarchyURL( aNewGroupURL
);
2582 size_t nCount
= pGroup
->count();
2583 for ( size_t i
= 0; i
< nCount
; i
++ )
2585 DocTemplates_EntryData_Impl
*pData
= pGroup
->getEntry( i
);
2586 addToHierarchy( pGroup
, pData
); // add entry to hierarchy
2592 void SfxDocTplService_Impl::removeFromHierarchy( GroupData_Impl
const *pGroup
)
2596 if ( Content::create( pGroup
->getHierarchyURL(), maCmdEnv
, comphelper::getProcessComponentContext(), aGroup
) )
2598 removeContent( aGroup
);
2603 GroupData_Impl::GroupData_Impl( const OUString
& rTitle
)
2604 : maTitle(rTitle
), mbInUse(false), mbInHierarchy(false)
2609 DocTemplates_EntryData_Impl
* GroupData_Impl::addEntry( const OUString
& rTitle
,
2610 const OUString
& rTargetURL
,
2611 const OUString
& rType
,
2612 const OUString
& rHierURL
)
2614 DocTemplates_EntryData_Impl
* pData
= nullptr;
2615 bool EntryFound
= false;
2617 for (auto const & p
: maEntries
)
2620 if ( pData
->getTitle() == rTitle
)
2629 pData
= new DocTemplates_EntryData_Impl( rTitle
);
2630 pData
->setTargetURL( rTargetURL
);
2631 pData
->setType( rType
);
2632 if ( !rHierURL
.isEmpty() )
2634 pData
->setHierarchyURL( rHierURL
);
2635 pData
->setHierarchy( true );
2637 maEntries
.emplace_back( pData
);
2641 if ( !rHierURL
.isEmpty() )
2643 pData
->setHierarchyURL( rHierURL
);
2644 pData
->setHierarchy( true );
2647 if ( pData
->getInHierarchy() )
2650 if ( rTargetURL
!= pData
->getTargetURL() )
2652 pData
->setTargetURL( rTargetURL
);
2653 pData
->setUpdateLink( true );
2661 DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( const OUString
& rTitle
)
2662 : maTitle(rTitle
), mbInHierarchy(false), mbInUse(false), mbUpdateType(false), mbUpdateLink(false)
2669 bool SfxURLRelocator_Impl::propertyCanContainOfficeDir(
2670 const OUString
& rPropName
)
2672 // Note: TargetURL is handled by UCB itself (because it is a property
2673 // with a predefined semantic). Additional Core properties introduced
2674 // be a client app must be handled by the client app itself, because
2675 // the UCB does not know the semantics of those properties.
2676 return ( rPropName
== TARGET_DIR_URL
|| rPropName
== PROPERTY_DIRLIST
);
2680 SfxURLRelocator_Impl::SfxURLRelocator_Impl( const uno::Reference
< XComponentContext
> & xContext
)
2681 : mxContext( xContext
)
2686 SfxURLRelocator_Impl::~SfxURLRelocator_Impl()
2691 void SfxURLRelocator_Impl::initOfficeInstDirs()
2693 if ( !mxOfficeInstDirs
.is() )
2695 osl::MutexGuard
aGuard( maMutex
);
2696 if ( !mxOfficeInstDirs
.is() )
2698 OSL_ENSURE( mxContext
.is(), "No service manager!" );
2700 mxOfficeInstDirs
= theOfficeInstallationDirectories::get(mxContext
);
2706 void SfxURLRelocator_Impl::implExpandURL( OUString
& io_url
)
2708 const INetURLObject
aParser( io_url
);
2709 if ( aParser
.GetProtocol() != INetProtocol::VndSunStarExpand
)
2712 io_url
= aParser
.GetURLPath( INetURLObject::DecodeMechanism::WithCharset
);
2715 if ( !mxMacroExpander
.is() )
2717 mxMacroExpander
.set( theMacroExpander::get(mxContext
), UNO_SET_THROW
);
2719 io_url
= mxMacroExpander
->expandMacros( io_url
);
2721 catch( const Exception
& )
2723 DBG_UNHANDLED_EXCEPTION("sfx.doc");
2728 void SfxURLRelocator_Impl::makeRelocatableURL( OUString
& rURL
)
2730 if ( !rURL
.isEmpty() )
2732 initOfficeInstDirs();
2733 implExpandURL( rURL
);
2734 rURL
= mxOfficeInstDirs
->makeRelocatableURL( rURL
);
2739 void SfxURLRelocator_Impl::makeAbsoluteURL( OUString
& rURL
)
2741 if ( !rURL
.isEmpty() )
2743 initOfficeInstDirs();
2744 implExpandURL( rURL
);
2745 rURL
= mxOfficeInstDirs
->makeAbsoluteURL( rURL
);
2749 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
2750 com_sun_star_comp_sfx2_DocumentTemplates_get_implementation(
2751 css::uno::XComponentContext
*context
,
2752 css::uno::Sequence
<css::uno::Any
> const &)
2754 return cppu::acquire(new SfxDocTplService(context
));
2757 OUString
DocTemplLocaleHelper::GetStandardGroupString()
2759 return SfxResId(TEMPLATE_LONG_NAMES_ARY
[0]);
2762 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */