Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / doc / doctemplates.cxx
blob1d17dc828f842f83f0df7ad28ef52b1ea7e60db1
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/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>
85 #include <memory>
86 #include <vector>
88 #define SERVICENAME_TYPEDETECTION "com.sun.star.document.TypeDetection"
90 #define TEMPLATE_ROOT_URL "vnd.sun.star.hier:/templates"
91 #define TITLE "Title"
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"
111 #define C_DELIM ';'
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;
127 using ::std::vector;
129 namespace {
131 class WaitWindow_Impl : public WorkWindow
133 tools::Rectangle maRect;
134 OUString maText;
135 static constexpr DrawTextFlags gnTextStyle = DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::WordBreak | DrawTextFlags::MultiLine;
137 public:
138 WaitWindow_Impl();
139 virtual ~WaitWindow_Impl() override;
140 virtual void dispose() override;
141 virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
144 #define X_OFFSET 15
145 #define Y_OFFSET 15
148 struct NamePair_Impl
150 OUString maShortName;
151 OUString maLongName;
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;
164 public:
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;
186 OUString maRootURL;
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;
195 void init_Impl();
196 void getDefaultLocale();
197 void getDirList();
198 void readFolderList();
199 bool needsUpdate();
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,
210 bool bCreateParent,
211 bool bFsysFolder,
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,
246 Any& rPropValue );
248 void createFromContent( GroupList_Impl& rList,
249 Content &rContent,
250 bool bHierarchy,
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;
277 public:
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 );
305 void update();
306 void doUpdate();
310 class DocTemplates_EntryData_Impl
312 OUString maTitle;
313 OUString maType;
314 OUString maTargetURL;
315 OUString maHierarchyURL;
317 bool mbInHierarchy : 1;
318 bool mbInUse : 1;
319 bool mbUpdateType : 1;
320 bool mbUpdateLink : 1;
322 public:
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; }
346 class GroupData_Impl
348 std::vector< std::unique_ptr<DocTemplates_EntryData_Impl> > maEntries;
349 OUString maTitle;
350 OUString maHierarchyURL;
351 OUString maTargetURL;
352 bool mbInUse : 1;
353 bool mbInHierarchy : 1;
355 public:
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;
391 if ( !mbLocaleSet )
392 getDefaultLocale();
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 ) )
404 uno::Any aValue;
405 OUString aPropValue;
406 if ( getProperty( maRootContent, aTemplVersPropName, aValue )
407 && ( aValue >>= aPropValue )
408 && aPropValue == aTemplVers )
410 bIsInitialized = true;
412 else
413 removeContent( maRootContent );
416 if ( !bIsInitialized )
418 if ( createFolder( maRootURL, true, false, maRootContent )
419 && setProperty( maRootContent, aTemplVersPropName, uno::makeAny( aTemplVers ) ) )
420 bIsInitialized = true;
422 bNeedsUpdate = true;
425 if ( bIsInitialized )
427 try {
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 );
436 getDirList();
437 readFolderList();
439 if ( bNeedsUpdate )
441 aGuard.clear();
442 SolarMutexClearableGuard aSolarGuard;
444 VclPtrInstance< WaitWindow_Impl > pWin;
445 aSolarGuard.clear();
447 osl::MutexGuard anotherGuard(maMutex);
448 update();
450 SolarMutexGuard aSecondSolarGuard;
452 pWin.disposeAndClear();
454 else if ( needsUpdate() )
455 // the UI should be shown only on the first update
456 update();
458 else
460 SAL_WARN( "sfx.doc", "init_Impl(): Could not create root" );
463 mbIsInitialized = bIsInitialized;
467 void SfxDocTplService_Impl::getDefaultLocale()
469 if ( !mbLocaleSet )
471 ::osl::MutexGuard aGuard( maMutex );
472 if ( !mbLocaleSet )
474 maLocale = LanguageTag::convertToLocale( utl::ConfigManager::getUILocale(), false);
475 mbLocaleSet = true;
480 const char* TEMPLATE_SHORT_NAMES_ARY[] =
482 "standard",
483 "styles",
484 "officorr",
485 "offimisc",
486 "personal",
487 "forms",
488 "finance",
489 "educate",
490 "layout",
491 "presnt",
492 "misc",
493 "labels"
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 )
515 OUString aRet;
517 for (auto const & pPair : maNames)
519 if ( pPair->maShortName == rShortName )
521 aRet = pPair->maLongName;
522 break;
526 if ( aRet.isEmpty() )
527 aRet = rShortName;
529 return aRet;
533 void SfxDocTplService_Impl::getDirList()
535 Any aValue;
537 // Get the template dir list
538 // TODO/LATER: let use service, register listener
539 INetURLObject aURL;
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:" );
549 sal_Int32 nIdx{ 0 };
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 ) };
559 if (nIndex<0)
560 continue;
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;
592 Any aValue;
594 // Get the template dir list
595 bool bHasProperty = getProperty( maRootContent, PROPERTY_NEEDSUPDATE, aValue );
597 if ( bHasProperty )
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;
603 if ( !bNeedsUpdate )
604 bNeedsUpdate = aTempCache.needsUpdate();
606 if ( bNeedsUpdate )
607 aTempCache.storeState();
609 return bNeedsUpdate;
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) },
627 { "URL", Any(rURL) }
628 }));
630 m_xDocProps->storeToStorage(xStorage, medium);
631 return true;
633 catch ( Exception& )
637 return false;
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();
652 catch ( Exception& )
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(
666 "MediaType",
667 OUString() );
669 catch( uno::Exception& )
673 if ( aTitle.isEmpty() )
675 INetURLObject aURL( rURL );
676 aURL.CutExtension();
677 aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
678 INetURLObject::DecodeMechanism::WithCharset );
680 else
681 bDocHasTitle = true;
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 )};
698 Content aLink;
700 if ( ! Content::create( aLinkURL, maCmdEnv, comphelper::getProcessComponentContext(), aLink ) )
702 Sequence< OUString > aNames(3);
703 aNames[0] = TITLE;
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 ) );
716 bAddedEntry = true;
718 catch( Exception& )
721 return bAddedEntry;
725 bool SfxDocTplService_Impl::createFolder( const OUString& rNewFolderURL,
726 bool bCreateParent,
727 bool bFsysFolder,
728 Content &rNewFolder )
730 Content aParent;
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
738 // like it
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);
751 aNames[0] = TITLE;
752 aNames[1] = IS_FOLDER;
754 Sequence< Any > aValues(2);
755 aValues[0] <<= aFolderName;
756 aValues[1] <<= true;
758 OUString aType;
760 if ( bFsysFolder )
761 aType = TYPE_FSYS_FOLDER;
762 else
763 aType = TYPE_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 );
799 Content aParent;
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;
806 if ( nInd )
807 aTryName += OUString::number( nInd );
811 Sequence< OUString > aNames(2);
812 aNames[0] = TITLE;
813 aNames[1] = IS_FOLDER;
815 Sequence< Any > aValues(2);
816 aValues[0] <<= aTryName;
817 aValues[1] <<= true;
819 bCreated = aParent.insertNewContent( TYPE_FSYS_FOLDER, aNames, aValues, aNewFolder );
821 catch( ucb::NameClashException& )
823 // if there is already an element, retry
825 catch( Exception& )
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 ) ) )
834 break;
837 if ( bCreated )
839 aNewFolderName = aTryName;
840 aNewFolderURL = aNewFolder.get()->getIdentifier()->getContentIdentifier();
841 break;
846 return bCreated;
850 OUString SfxDocTplService_Impl::CreateNewUniqueFileWithPrefix( const OUString& aPath,
851 const OUString& aPrefix,
852 const OUString& aExt )
854 OUString aNewFileURL;
855 INetURLObject aDirPath( aPath );
857 Content aParent;
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++ )
864 Content aNewFile;
865 bool bCreated = false;
866 OUString aTryName = aPrefix;
867 if ( nInd )
868 aTryName += OUString::number( nInd );
869 if ( aExt.toChar() != '.' )
870 aTryName += ".";
871 aTryName += aExt;
875 Sequence< OUString > aNames(2);
876 aNames[0] = TITLE;
877 aNames[1] = IS_DOCUMENT;
879 Sequence< Any > aValues(2);
880 aValues[0] <<= aTryName;
881 aValues[1] <<= true;
883 bCreated = aParent.insertNewContent( TYPE_FSYS_FILE, aNames, aValues, aNewFile );
885 catch( ucb::NameClashException& )
887 // if there is already an element, retry
889 catch( Exception& )
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 ) ) )
898 break;
901 if ( bCreated )
903 aNewFileURL = aNewFile.get()->getIdentifier()->getContentIdentifier();
904 break;
909 return aNewFileURL;
913 bool SfxDocTplService_Impl::removeContent( Content& rContent )
915 bool bRemoved = false;
918 Any aArg = makeAny( true );
920 rContent.executeCommand( COMMAND_DELETE, aArg );
921 bRemoved = true;
923 catch ( RuntimeException& ) {}
924 catch ( Exception& ) {}
926 return bRemoved;
930 bool SfxDocTplService_Impl::removeContent( const OUString& rContentURL )
932 Content aContent;
934 if ( Content::create( rContentURL, maCmdEnv, comphelper::getProcessComponentContext(), aContent ) )
935 return removeContent( aContent );
936 return false;
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 ) )
976 OUString aValue;
977 if ( rPropValue >>= aValue )
979 maRelocator.makeRelocatableURL( aValue );
980 aPropValue <<= aValue;
982 else
984 Sequence< OUString > aValues;
985 if ( rPropValue >>= aValues )
987 for ( auto& rValue : aValues )
989 maRelocator.makeRelocatableURL( rValue );
991 aPropValue <<= aValues;
993 else
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;
1016 // Get the property
1019 uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
1021 // check, whether or not the property exists
1022 if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
1024 return false;
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 ) )
1035 OUString aValue;
1036 if ( rPropValue >>= aValue )
1038 maRelocator.makeAbsoluteURL( aValue );
1039 rPropValue <<= aValue;
1041 else
1043 Sequence< OUString > aValues;
1044 if ( rPropValue >>= aValues )
1046 for ( auto& rValue : aValues )
1048 maRelocator.makeAbsoluteURL( rValue );
1050 rPropValue <<= aValues;
1052 else
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 );
1076 maNames.clear();
1080 lang::Locale SfxDocTplService_Impl::getLocale()
1082 ::osl::MutexGuard aGuard( maMutex );
1084 if ( !mbLocaleSet )
1085 getDefaultLocale();
1087 return maLocale;
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;
1101 maLocale = rLocale;
1102 mbLocaleSet = true;
1106 void SfxDocTplService_Impl::update()
1108 ::osl::MutexGuard aGuard( maMutex );
1110 doUpdate();
1114 void SfxDocTplService_Impl::doUpdate()
1116 ::osl::MutexGuard aGuard( maMutex );
1118 const OUString aPropName( PROPERTY_NEEDSUPDATE );
1119 Any aValue;
1121 aValue <<= true;
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;
1140 while ( nCountDir )
1142 nCountDir--;
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() )
1158 Content aGroup;
1159 if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1160 setProperty( aGroup,
1161 TARGET_DIR_URL,
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
1172 else
1173 addToHierarchy( pGroup.get(), pData ); // add entry to hierarchy
1175 else if ( pData->getUpdateType() ||
1176 pData->getUpdateLink() )
1178 updateData( pData );
1182 else
1184 addGroupToHierarchy( pGroup.get() ); // add group to hierarchy
1187 else
1188 removeFromHierarchy( pGroup.get() ); // delete group from hierarchy
1190 aGroupList.clear();
1192 aValue <<= false;
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& )
1219 return aUINames;
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 )
1233 return false;
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;
1256 bChanged = true;
1259 if ( !bChanged )
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 )
1280 bChanged = true;
1281 else
1283 nNewLen++;
1284 aNewUINames[nNewLen-1].First = aUINames[nInd].First;
1285 aNewUINames[nNewLen-1].Second = aUINames[nInd].Second;
1288 aNewUINames.resize( nNewLen );
1290 if (bChanged)
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;
1299 try {
1300 uno::Reference< beans::XPropertySet > xTempFile(
1301 io::TempFile::create(mxContext),
1302 uno::UNO_QUERY_THROW );
1304 OUString aTempURL;
1305 uno::Any aUrl = xTempFile->getPropertyValue("Uri");
1306 aUrl >>= aTempURL;
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);
1314 try {
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,
1324 "groupuinames.xml",
1325 ucb::NameClash::OVERWRITE,
1326 "text/xml" );
1327 bResult = true;
1329 catch ( uno::Exception& )
1333 return bResult;
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
1346 Content aNewFolder;
1347 OUString aNewFolderName;
1349 // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1350 if ( !CreateNewUniqueFolderWithPrefix( aTargetPath,
1351 rGroupName,
1352 aNewFolderName,
1353 aResultURL,
1354 aNewFolder )
1355 && !CreateNewUniqueFolderWithPrefix( aTargetPath,
1356 "UserGroup",
1357 aNewFolderName,
1358 aResultURL,
1359 aNewFolder ) )
1361 return OUString();
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 );
1368 return OUString();
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 );
1377 return OUString();
1381 return aResultURL;
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
1390 Content aNewGroup;
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
1405 return false;
1408 // Get the user template path entry ( new group will always
1409 // be added in the user template path )
1410 sal_Int32 nIndex;
1411 OUString aUserPath;
1413 nIndex = maTemplateDirs.getLength();
1414 if ( nIndex )
1415 nIndex--;
1416 else
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
1422 Content aNewFolder;
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,
1428 rGroupName,
1429 aNewFolderName,
1430 aNewFolderURL,
1431 aNewFolder )
1432 && !CreateNewUniqueFolderWithPrefix( aUserPath,
1433 "UserGroup",
1434 aNewFolderName,
1435 aNewFolderURL,
1436 aNewFolder ) )
1438 // we could not create the folder, so we delete the group in the
1439 // hierarchy and return
1440 removeContent( aNewGroup );
1441 return false;
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 );
1450 return false;
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 );
1460 return false;
1463 return true;
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
1483 Content aGroup;
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 );
1489 Any aValue;
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() )
1499 return false;
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())
1504 return false;
1506 OUString aGeneralTempPath = findParentTemplateDir(
1507 aGroupParentFolder.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1509 if (aGeneralTempPath.isEmpty())
1510 return false;
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 );
1538 else
1539 bHasNonRemovable = true;
1541 else
1542 bHasShared = 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& ) {}
1569 return bResult;
1573 bool SfxDocTplService_Impl::renameGroup( const OUString& rOldName,
1574 const OUString& rNewName )
1576 ::osl::MutexGuard aGuard( maMutex );
1578 // create the group url
1579 Content aGroup;
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
1587 // if there is one.
1588 if ( Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1589 return false;
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 ) )
1599 return false;
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
1604 Any aValue;
1605 if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
1606 aValue >>= aGroupTargetURL;
1608 if ( aGroupTargetURL.isEmpty() )
1609 return false;
1611 if ( !maTemplateDirs.hasElements() )
1612 return false;
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)))
1619 return false;
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 ),
1653 aFsysName,
1654 rOldName,
1655 rNewName ) )
1657 // rename the group in the hierarchy
1658 Any aTitleValue;
1659 aTitleValue <<= rNewName;
1661 return setProperty( aGroup, TITLE, aTitleValue );
1665 return false;
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 ) )
1687 return false;
1689 OUString aGroupTargetURL;
1690 Any aValue;
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/" ))}
1734 }));
1735 uno::Reference< container::XNameAccess > xSOFConfig(
1736 xConfigProvider->createInstanceWithArguments(
1737 "com.sun.star.configuration.ConfigurationAccess",
1738 aArgs ),
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;
1758 OUString aTypeName;
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 =
1768 mxType.is() ?
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();
1804 // store template
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 );
1813 else
1814 rStorable->store();
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 );
1822 * pb: #i79496#
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 );
1839 catch( Exception& )
1841 // the template was not stored
1842 return false;
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 ) )
1864 return false;
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 ) )
1874 return false;
1876 // get the target url of the group
1877 OUString aTargetURL;
1878 Any aValue;
1880 if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
1881 aValue >>= aTargetURL;
1883 if ( aTargetURL.isEmpty() )
1885 aTargetURL = CreateNewGroupFsys( rGroupName, aGroup );
1887 if ( aTargetURL.isEmpty() )
1888 return false;
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
1902 // this scenario
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() )
1928 return false;
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 ) )
1935 return false;
1937 if( ! Content::create( aTargetURL, xEnv, comphelper::getProcessComponentContext(), aTargetGroup ) )
1938 return false;
1940 // transfer source file
1943 aTargetGroup.transferContent( aSourceContent,
1944 InsertOperation::Copy,
1945 aNewTemplateTargetName,
1946 NameClash::OVERWRITE,
1947 aType );
1949 // allow to edit the added template
1950 Content aResultContent;
1951 if ( Content::create( aNewTemplateTargetURL, xEnv, comphelper::getProcessComponentContext(), aResultContent ) )
1953 const OUString aPropertyName( "IsReadOnly" );
1954 uno::Any aProperty;
1955 bool bReadOnly = false;
1956 if ( getProperty( aResultContent, aPropertyName, aProperty ) && ( aProperty >>= bReadOnly ) && bReadOnly )
1957 setProperty( aResultContent, aPropertyName, uno::makeAny( false ) );
1960 catch ( ContentCreationException& )
1961 { return false; }
1962 catch ( Exception& )
1963 { return false; }
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 );
1990 return false;
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())
2004 return *pDirs;
2005 return OUString();
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 ) )
2024 return false;
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 ) )
2034 return false;
2036 // get the target URL from the template
2037 OUString aTargetURL;
2038 Any aValue;
2040 if ( getProperty( aTemplate, TARGET_URL, aValue ) )
2041 aValue >>= aTargetURL;
2043 // delete the target template
2044 if ( !aTargetURL.isEmpty() )
2046 if (isInternalTemplateDir(aTargetURL))
2047 return false;
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 ) )
2074 return false;
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 ) )
2084 return false;
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 ) )
2095 return false;
2097 OUString aTemplateTargetURL;
2098 Any aTargetValue;
2100 if ( getProperty( aTemplate, TARGET_URL, aTargetValue ) )
2101 aTargetValue >>= aTemplateTargetURL;
2103 if ( !setTitleForURL( aTemplateTargetURL, rNewName ) )
2104 return false;
2106 // rename the template entry in the cache
2107 Any aTitleValue;
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;
2118 public:
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" };
2134 return aSeq;
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()
2189 if ( pImp->init() )
2190 return pImp->getContent().get();
2191 return nullptr;
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 )
2223 return true;
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 )
2245 return true;
2247 return pImp->init() && pImp->renameGroup( rOldName, rNewName );
2251 void SAL_CALL SfxDocTplService::update()
2253 if ( pImp->init() )
2254 pImp->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);
2262 aRect = maRect;
2263 aRect.AdjustRight(2 * X_OFFSET );
2264 aRect.AdjustBottom(2 * Y_OFFSET );
2265 maRect.SetPos(Point(X_OFFSET, Y_OFFSET));
2266 SetOutputSizePixel(aRect.GetSize());
2268 Show();
2269 PaintImmediately();
2270 Flush();
2274 WaitWindow_Impl::~WaitWindow_Impl()
2276 disposeOnce();
2279 void WaitWindow_Impl::dispose()
2281 Hide();
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
2296 Content aContent;
2297 uno::Reference<XResultSet> xResultSet;
2298 Sequence<OUString> aProps(3);
2300 aProps[0] = TITLE;
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() )
2316 return;
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() )
2340 OUString aTmpTitle;
2342 bool bDocHasTitle = false;
2343 getTitleFromURL( aTargetDir, aTmpTitle, aType, bDocHasTitle );
2345 if ( !aType.isEmpty() )
2346 bUpdateType = true;
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 )
2363 OUString aTitle;
2365 if ( rUITitle.isEmpty() )
2367 // reserved FS names that should not be used
2368 if ( rTitle == "wizard" )
2369 return;
2370 else if ( rTitle == "internal" )
2371 return;
2373 aTitle = getLongName( rTitle );
2375 else
2376 aTitle = rUITitle;
2378 if ( aTitle.isEmpty() )
2379 return;
2381 GroupData_Impl* pGroup = nullptr;
2382 for (const std::unique_ptr<GroupData_Impl>& i : rList)
2384 if ( i->getTitle() == aTitle )
2386 pGroup = i.get();
2387 break;
2391 if ( !pGroup )
2393 pGroup = new GroupData_Impl( aTitle );
2394 rList.push_back( std::unique_ptr<GroupData_Impl>(pGroup) );
2397 if ( bWriteableGroup )
2398 pGroup->setTargetURL( rOwnURL );
2400 pGroup->setInUse();
2402 // now get the content of the Group
2403 Content aContent;
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() )
2418 return;
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()};
2429 OUString aType;
2431 if ( aChildTitle == "sfx.tlx" || aChildTitle == "groupuinames.xml" )
2432 continue;
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,
2445 Content &rContent,
2446 bool bHierarchy,
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
2452 if ( ! bHierarchy )
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;
2463 if ( !bHierarchy )
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() )
2476 return;
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() );
2489 if ( bHierarchy )
2490 addHierGroup( rList, aTitle, aTargetSubfolderURL );
2491 else
2493 OUString aUITitle;
2494 for (const beans::StringPair & rUIName : aUINames)
2495 if ( rUIName.First == aTitle )
2497 aUITitle = rUIName.Second;
2498 break;
2501 addFsysGroup( rList, aTitle, aUITitle, aTargetSubfolderURL, bWriteableContent );
2505 catch ( Exception& ) {}
2509 void SfxDocTplService_Impl::removeFromHierarchy( DocTemplates_EntryData_Impl const *pData )
2511 Content aTemplate;
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 ) )
2526 return;
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 ) )
2539 return;
2541 addEntry( aGroup, pData->getTitle(),
2542 pData->getTargetURL(),
2543 pData->getType() );
2547 void SfxDocTplService_Impl::updateData( DocTemplates_EntryData_Impl const *pData )
2549 Content aTemplate;
2551 if ( ! Content::create( pData->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
2552 return;
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 )
2568 Content aGroup;
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 )
2594 Content aGroup;
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)
2619 pData = p.get();
2620 if ( pData->getTitle() == rTitle )
2622 EntryFound = true;
2623 break;
2627 if ( !EntryFound )
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 );
2639 else
2641 if ( !rHierURL.isEmpty() )
2643 pData->setHierarchyURL( rHierURL );
2644 pData->setHierarchy( true );
2647 if ( pData->getInHierarchy() )
2648 pData->setInUse();
2650 if ( rTargetURL != pData->getTargetURL() )
2652 pData->setTargetURL( rTargetURL );
2653 pData->setUpdateLink( true );
2657 return pData;
2661 DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( const OUString& rTitle )
2662 : maTitle(rTitle), mbInHierarchy(false), mbInUse(false), mbUpdateType(false), mbUpdateLink(false)
2668 // static
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 )
2710 return;
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: */