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