1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <osl/mutex.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <tools/urlobj.hxx>
23 #include <rtl/ustring.hxx>
24 #include <rtl/ustrbuf.hxx>
25 #include <sal/log.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/wrkwin.hxx>
28 #include <unotools/pathoptions.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/propertysequence.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/util/theMacroExpander.hpp>
44 #include <com/sun/star/util/theOfficeInstallationDirectories.hpp>
45 #include <com/sun/star/configuration/theDefaultProvider.hpp>
46 #include <com/sun/star/container/XContainerQuery.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/TransferInfo.hpp>
56 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
57 #include <com/sun/star/ucb/XContentAccess.hpp>
58 #include <com/sun/star/frame/ModuleManager.hpp>
59 #include <com/sun/star/uno/Exception.hpp>
60 #include <com/sun/star/task/InteractionHandler.hpp>
61 #include <com/sun/star/ucb/XProgressHandler.hpp>
62 #include <com/sun/star/container/XNameAccess.hpp>
63 #include <com/sun/star/frame/XDocumentTemplates.hpp>
64 #include <com/sun/star/frame/XStorable.hpp>
65 #include <com/sun/star/lang/Locale.hpp>
66 #include <com/sun/star/lang/XLocalizable.hpp>
67 #include <com/sun/star/lang/XServiceInfo.hpp>
68 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
69 #include <com/sun/star/ucb/XContent.hpp>
70 #include <com/sun/star/beans/PropertyValue.hpp>
71 #include <com/sun/star/uno/RuntimeException.hpp>
72 #include <com/sun/star/uno/XComponentContext.hpp>
73 #include <com/sun/star/util/thePathSettings.hpp>
74 
75 #include <rtl/ref.hxx>
76 #include <svtools/templatefoldercache.hxx>
77 #include <unotools/configmgr.hxx>
78 #include <unotools/ucbhelper.hxx>
79 #include <i18nlangtag/languagetag.hxx>
80 #include <ucbhelper/content.hxx>
81 
82 #include <sfx2/sfxresid.hxx>
83 #include <sfxurlrelocator.hxx>
84 #include "doctemplateslocal.hxx"
85 #include <sfx2/docfac.hxx>
86 #include <sfx2/docfile.hxx>
87 #include <sfx2/strings.hrc>
88 #include <doctempl.hrc>
89 
90 #include <memory>
91 #include <vector>
92 
93 #define SERVICENAME_TYPEDETECTION "com.sun.star.document.TypeDetection"
94 
95 #define TEMPLATE_ROOT_URL       "vnd.sun.star.hier:/templates"
96 #define TITLE                   "Title"
97 #define IS_FOLDER               "IsFolder"
98 #define IS_DOCUMENT             "IsDocument"
99 #define TARGET_URL              "TargetURL"
100 #define TEMPLATE_VERSION        "TemplateComponentVersion"
101 #define TEMPLATE_VERSION_VALUE  "2"
102 #define TYPE_FOLDER             "application/vnd.sun.star.hier-folder"
103 #define TYPE_LINK               "application/vnd.sun.star.hier-link"
104 #define TYPE_FSYS_FOLDER        "application/vnd.sun.staroffice.fsys-folder"
105 #define TYPE_FSYS_FILE          "application/vnd.sun.staroffice.fsys-file"
106 
107 #define PROPERTY_DIRLIST        "DirectoryList"
108 #define PROPERTY_NEEDSUPDATE    "NeedsUpdate"
109 #define PROPERTY_TYPE           "TypeDescription"
110 
111 #define TARGET_DIR_URL          "TargetDirURL"
112 #define COMMAND_DELETE          "delete"
113 
114 #define STANDARD_FOLDER         "standard"
115 
116 #define C_DELIM                 ';'
117 
118 using namespace ::com::sun::star;
119 using namespace ::com::sun::star::beans;
120 using namespace ::com::sun::star::document;
121 using namespace ::com::sun::star::io;
122 using namespace ::com::sun::star::lang;
123 using namespace ::com::sun::star::sdbc;
124 using namespace ::com::sun::star::ucb;
125 using namespace ::com::sun::star::uno;
126 using namespace ::com::sun::star::container;
127 using namespace ::com::sun::star::util;
128 
129 using namespace ::ucbhelper;
130 using namespace ::comphelper;
131 
132 using ::std::vector;
133 
134 namespace {
135 
136 class WaitWindow_Impl : public WorkWindow
137 {
138     tools::Rectangle     maRect;
139     OUString      maText;
140     static constexpr DrawTextFlags gnTextStyle = DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::WordBreak | DrawTextFlags::MultiLine;
141 
142 public:
143     WaitWindow_Impl();
144     virtual ~WaitWindow_Impl() override;
145     virtual void dispose() override;
146     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
147 };
148 
149 #define X_OFFSET 15
150 #define Y_OFFSET 15
151 
152 
153 struct NamePair_Impl
154 {
155     OUString maShortName;
156     OUString maLongName;
157 };
158 
159 class DocTemplates_EntryData_Impl;
160 class GroupData_Impl;
161 
162 typedef vector< std::unique_ptr<GroupData_Impl> > GroupList_Impl;
163 
164 
165 class TplTaskEnvironment : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment >
166 {
167     uno::Reference< task::XInteractionHandler >               m_xInteractionHandler;
168 
169 public:
TplTaskEnvironment(const uno::Reference<task::XInteractionHandler> & rxInteractionHandler)170     explicit TplTaskEnvironment( const uno::Reference< task::XInteractionHandler>& rxInteractionHandler )
171                                 : m_xInteractionHandler( rxInteractionHandler )
172                             {}
173 
getInteractionHandler()174     virtual uno::Reference<task::XInteractionHandler> SAL_CALL getInteractionHandler() override
175     { return m_xInteractionHandler; }
176 
getProgressHandler()177     virtual uno::Reference<ucb::XProgressHandler> SAL_CALL    getProgressHandler() override
178     { return uno::Reference<ucb::XProgressHandler>(); }
179 };
180 
181 class SfxDocTplService_Impl
182 {
183     uno::Reference< XComponentContext >              mxContext;
184     uno::Reference< XCommandEnvironment >            maCmdEnv;
185     uno::Reference< XDocumentProperties>             m_xDocProps;
186     uno::Reference< XTypeDetection >                 mxType;
187 
188     ::osl::Mutex                maMutex;
189     Sequence< OUString >        maTemplateDirs;
190     Sequence< OUString >        maInternalTemplateDirs;
191     OUString                    maRootURL;
192     std::vector< std::unique_ptr<NamePair_Impl> > maNames;
193     lang::Locale                maLocale;
194     Content                     maRootContent;
195     bool                        mbIsInitialized : 1;
196     bool                        mbLocaleSet     : 1;
197 
198     SfxURLRelocator_Impl        maRelocator;
199 
200     void                        init_Impl();
201     void                        getDefaultLocale();
202     void                        getDirList();
203     void                        readFolderList();
204     bool                        needsUpdate();
205     OUString                    getLongName( const OUString& rShortName );
206     bool                    setTitleForURL( const OUString& rURL, const OUString& aTitle );
207     void                    getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, bool& bDocHasTitle );
208 
209     bool                    addEntry( Content& rParentFolder,
210                                           const OUString& rTitle,
211                                           const OUString& rTargetURL,
212                                           const OUString& rType );
213 
214     bool                    createFolder( const OUString& rNewFolderURL,
215                                               bool  bCreateParent,
216                                               bool  bFsysFolder,
217                                               Content   &rNewFolder );
218 
219     static bool             CreateNewUniqueFolderWithPrefix( const OUString& aPath,
220                                                                 const OUString& aPrefix,
221                                                                 OUString& aNewFolderName,
222                                                                 OUString& aNewFolderURL,
223                                                                 Content& aNewFolder );
224     static OUString         CreateNewUniqueFileWithPrefix( const OUString& aPath,
225                                                                 const OUString& aPrefix,
226                                                                 const OUString& aExt );
227 
228     std::vector< beans::StringPair > ReadUINamesForTemplateDir_Impl( const OUString& aUserPath );
229     bool                    UpdateUINamesForTemplateDir_Impl( const OUString& aUserPath,
230                                                                   const OUString& aGroupName,
231                                                                   const OUString& aNewFolderName );
232     bool                    ReplaceUINamesForTemplateDir_Impl( const OUString& aUserPath,
233                                                                   const OUString& aFsysGroupName,
234                                                                   const OUString& aOldGroupName,
235                                                                   const OUString& aNewGroupName );
236     void                    RemoveUINamesForTemplateDir_Impl( const OUString& aUserPath,
237                                                                   const OUString& aGroupName );
238     bool                    WriteUINamesForTemplateDir_Impl( const OUString& aUserPath,
239                                                                 const std::vector< beans::StringPair >& aUINames );
240 
241     OUString                CreateNewGroupFsys( const OUString& rGroupName, Content& aGroup );
242 
243     static bool             removeContent( Content& rContent );
244     bool                    removeContent( const OUString& rContentURL );
245 
246     bool                    setProperty( Content& rContent,
247                                              const OUString& rPropName,
248                                              const Any& rPropValue );
249     bool                    getProperty( Content& rContent,
250                                              const OUString& rPropName,
251                                              Any& rPropValue );
252 
253     void                        createFromContent( GroupList_Impl& rList,
254                                                    Content &rContent,
255                                                    bool bHierarchy,
256                                                    bool bWriteableContent );
257     void                        addHierGroup( GroupList_Impl& rList,
258                                               const OUString& rTitle,
259                                               const OUString& rOwnURL );
260     void                        addFsysGroup( GroupList_Impl& rList,
261                                               const OUString& rTitle,
262                                               const OUString& rUITitle,
263                                               const OUString& rOwnURL,
264                                               bool bWriteableGroup );
265     void                        removeFromHierarchy( DocTemplates_EntryData_Impl const *pData );
266     void                        addToHierarchy( GroupData_Impl const *pGroup,
267                                                 DocTemplates_EntryData_Impl const *pData );
268 
269     void                        removeFromHierarchy( GroupData_Impl const *pGroup );
270     void                        addGroupToHierarchy( GroupData_Impl *pGroup );
271 
272     void                        updateData( DocTemplates_EntryData_Impl const *pData );
273 
274     //See: #i66157# and rhbz#1065807
275     //return which template dir the rURL is a subpath of
276     OUString                    findParentTemplateDir(const OUString& rURL) const;
277 
278     //See: #i66157# and rhbz#1065807
279     //return true if rURL is a path (or subpath of) a dir which is not a user path
280     //which implies neither it or its contents can be removed
281     bool                        isInternalTemplateDir(const OUString& rURL) const;
282 public:
283     explicit                    SfxDocTplService_Impl( const uno::Reference< XComponentContext > & xContext );
284                                 ~SfxDocTplService_Impl();
285 
init()286     bool                        init() { if ( !mbIsInitialized ) init_Impl(); return mbIsInitialized; }
getContent() const287     const Content&              getContent() const { return maRootContent; }
288 
289     void                        setLocale( const lang::Locale & rLocale );
290     lang::Locale                getLocale();
291 
292     bool                        storeTemplate( const OUString& rGroupName,
293                                                const OUString& rTemplateName,
294                                                const uno::Reference< frame::XStorable >& rStorable );
295 
296     bool                        addTemplate( const OUString& rGroupName,
297                                              const OUString& rTemplateName,
298                                              const OUString& rSourceURL );
299     bool                        removeTemplate( const OUString& rGroupName,
300                                                 const OUString& rTemplateName );
301     bool                        renameTemplate( const OUString& rGroupName,
302                                                 const OUString& rOldName,
303                                                 const OUString& rNewName );
304 
305     bool                        addGroup( const OUString& rGroupName );
306     bool                        removeGroup( const OUString& rGroupName );
307     bool                        renameGroup( const OUString& rOldName,
308                                              const OUString& rNewName );
309 
310     void                        update();
311     void                        doUpdate();
312 };
313 
314 
315 class DocTemplates_EntryData_Impl
316 {
317     OUString            maTitle;
318     OUString            maType;
319     OUString            maTargetURL;
320     OUString            maHierarchyURL;
321 
322     bool            mbInHierarchy   : 1;
323     bool            mbInUse         : 1;
324     bool            mbUpdateType    : 1;
325     bool            mbUpdateLink    : 1;
326 
327 public:
328    explicit             DocTemplates_EntryData_Impl( const OUString& rTitle );
329 
setInUse()330     void                setInUse() { mbInUse = true; }
setHierarchy(bool bInHierarchy)331     void                setHierarchy( bool bInHierarchy ) { mbInHierarchy = bInHierarchy; }
setUpdateLink(bool bUpdateLink)332     void                setUpdateLink( bool bUpdateLink ) { mbUpdateLink = bUpdateLink; }
setUpdateType(bool bUpdateType)333     void                setUpdateType( bool bUpdateType ) { mbUpdateType = bUpdateType; }
334 
getInUse() const335     bool                getInUse() const { return mbInUse; }
getInHierarchy() const336     bool                getInHierarchy() const { return mbInHierarchy; }
getUpdateLink() const337     bool                getUpdateLink() const { return mbUpdateLink; }
getUpdateType() const338     bool                getUpdateType() const { return mbUpdateType; }
339 
getHierarchyURL() const340     const OUString&     getHierarchyURL() const { return maHierarchyURL; }
getTargetURL() const341     const OUString&     getTargetURL() const { return maTargetURL; }
getTitle() const342     const OUString&     getTitle() const { return maTitle; }
getType() const343     const OUString&     getType() const { return maType; }
344 
setHierarchyURL(const OUString & rURL)345     void                setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; }
setTargetURL(const OUString & rURL)346     void                setTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
setType(const OUString & rType)347     void                setType( const OUString& rType ) { maType = rType; }
348 };
349 
350 
351 class GroupData_Impl
352 {
353     std::vector< std::unique_ptr<DocTemplates_EntryData_Impl> > maEntries;
354     OUString            maTitle;
355     OUString            maHierarchyURL;
356     OUString            maTargetURL;
357     bool            mbInUse         : 1;
358     bool            mbInHierarchy   : 1;
359 
360 public:
361     explicit            GroupData_Impl( const OUString& rTitle );
362 
setInUse()363     void                setInUse() { mbInUse = true; }
setHierarchy(bool bInHierarchy)364     void                setHierarchy( bool bInHierarchy ) { mbInHierarchy = bInHierarchy; }
setHierarchyURL(const OUString & rURL)365     void                setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; }
setTargetURL(const OUString & rURL)366     void                setTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
367 
getInUse() const368     bool            getInUse() const { return mbInUse; }
getInHierarchy() const369     bool            getInHierarchy() const { return mbInHierarchy; }
getHierarchyURL() const370     const OUString&     getHierarchyURL() const { return maHierarchyURL; }
getTargetURL() const371     const OUString&     getTargetURL() const { return maTargetURL; }
getTitle() const372     const OUString&     getTitle() const { return maTitle; }
373 
374     DocTemplates_EntryData_Impl*     addEntry( const OUString& rTitle,
375                                   const OUString& rTargetURL,
376                                   const OUString& rType,
377                                   const OUString& rHierURL );
count()378     size_t                          count() { return maEntries.size(); }
getEntry(size_t nPos)379     DocTemplates_EntryData_Impl*    getEntry( size_t nPos ) { return maEntries[ nPos ].get(); }
380 };
381 
382 
383 // private SfxDocTplService_Impl
384 
init_Impl()385 void SfxDocTplService_Impl::init_Impl()
386 {
387     uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
388     uno::Reference < task::XInteractionHandler > xInteractionHandler(
389         task::InteractionHandler::createWithParent(xContext, nullptr), uno::UNO_QUERY_THROW );
390     maCmdEnv = new TplTaskEnvironment( xInteractionHandler );
391 
392     ::osl::ClearableMutexGuard aGuard( maMutex );
393     bool bIsInitialized = false;
394     bool bNeedsUpdate   = false;
395 
396     if ( !mbLocaleSet )
397         getDefaultLocale();
398 
399     // convert locale to string
400     // set maRootContent to the root of the templates hierarchy. Create the
401     // entry if necessary
402 
403     maRootURL = ( TEMPLATE_ROOT_URL "/" ) + LanguageTag::convertToBcp47(maLocale);
404 
405     const OUString aTemplVersPropName( TEMPLATE_VERSION  );
406     const OUString aTemplVers( TEMPLATE_VERSION_VALUE  );
407     if ( Content::create( maRootURL, maCmdEnv, comphelper::getProcessComponentContext(), maRootContent ) )
408     {
409         uno::Any aValue;
410         OUString aPropValue;
411         if ( getProperty( maRootContent, aTemplVersPropName, aValue )
412           && ( aValue >>= aPropValue )
413           && aPropValue == aTemplVers )
414         {
415             bIsInitialized = true;
416         }
417         else
418             removeContent( maRootContent );
419     }
420 
421     if ( !bIsInitialized )
422     {
423         if ( createFolder( maRootURL, true, false, maRootContent )
424           && setProperty( maRootContent, aTemplVersPropName, uno::makeAny( aTemplVers ) ) )
425             bIsInitialized = true;
426 
427         bNeedsUpdate = true;
428     }
429 
430     if ( bIsInitialized )
431     {
432         try {
433             m_xDocProps.set(document::DocumentProperties::create(
434                         ::comphelper::getProcessComponentContext()));
435         } catch (uno::RuntimeException const&) {
436             TOOLS_WARN_EXCEPTION("sfx.doc", "SfxDocTplService_Impl::init_Impl: cannot create DocumentProperties service:");
437         }
438 
439         OUString const aService = SERVICENAME_TYPEDETECTION;
440         mxType.set( mxContext->getServiceManager()->createInstanceWithContext(aService, mxContext), UNO_QUERY );
441 
442         getDirList();
443         readFolderList();
444 
445         if ( bNeedsUpdate )
446         {
447             aGuard.clear();
448             SolarMutexClearableGuard aSolarGuard;
449 
450             VclPtrInstance< WaitWindow_Impl > pWin;
451             aSolarGuard.clear();
452             {
453                 osl::MutexGuard anotherGuard(maMutex);
454                 update();
455             }
456             SolarMutexGuard aSecondSolarGuard;
457 
458             pWin.disposeAndClear();
459         }
460         else if ( needsUpdate() )
461             // the UI should be shown only on the first update
462             update();
463     }
464     else
465     {
466         SAL_WARN( "sfx.doc", "init_Impl(): Could not create root" );
467     }
468 
469     mbIsInitialized = bIsInitialized;
470 }
471 
472 
getDefaultLocale()473 void SfxDocTplService_Impl::getDefaultLocale()
474 {
475     if ( !mbLocaleSet )
476     {
477         ::osl::MutexGuard aGuard( maMutex );
478         if ( !mbLocaleSet )
479         {
480             maLocale = LanguageTag::convertToLocale( utl::ConfigManager::getUILocale(), false);
481             mbLocaleSet = true;
482         }
483     }
484 }
485 
486 const char* TEMPLATE_SHORT_NAMES_ARY[] =
487 {
488     "standard",
489     "officorr",
490     "offimisc",
491     "personal",
492     "forms",
493     "finance",
494     "educate",
495     "layout",
496     "presnt",
497     "misc",
498     "labels",
499     "styles"
500 };
501 
readFolderList()502 void SfxDocTplService_Impl::readFolderList()
503 {
504     SolarMutexGuard aGuard;
505 
506     static_assert( SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY) == SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY), "mismatch array lengths" );
507     const size_t nCount = std::min(SAL_N_ELEMENTS(TEMPLATE_SHORT_NAMES_ARY), SAL_N_ELEMENTS(TEMPLATE_LONG_NAMES_ARY));
508     for (size_t i = 0; i < nCount; ++i)
509     {
510         std::unique_ptr<NamePair_Impl> pPair( new NamePair_Impl );
511         pPair->maShortName  = OUString::createFromAscii(TEMPLATE_SHORT_NAMES_ARY[i]);
512         pPair->maLongName   = SfxResId(TEMPLATE_LONG_NAMES_ARY[i]);
513 
514         maNames.push_back( std::move(pPair) );
515     }
516 }
517 
518 
getLongName(const OUString & rShortName)519 OUString SfxDocTplService_Impl::getLongName( const OUString& rShortName )
520 {
521     OUString         aRet;
522 
523     for (auto const & pPair : maNames)
524     {
525         if ( pPair->maShortName == rShortName )
526         {
527             aRet = pPair->maLongName;
528             break;
529         }
530     }
531 
532     if ( aRet.isEmpty() )
533         aRet = rShortName;
534 
535     return aRet;
536 }
537 
538 
getDirList()539 void SfxDocTplService_Impl::getDirList()
540 {
541     Any      aValue;
542 
543     // Get the template dir list
544     // TODO/LATER: let use service, register listener
545     INetURLObject   aURL;
546     OUString    aDirs = SvtPathOptions().GetTemplatePath();
547     sal_Int32 nCount = comphelper::string::getTokenCount(aDirs, C_DELIM);
548 
549     maTemplateDirs = Sequence< OUString >( nCount );
550 
551     uno::Reference< util::XMacroExpander > xExpander = util::theMacroExpander::get(mxContext);
552     const OUString aPrefix(
553         "vnd.sun.star.expand:"  );
554 
555     sal_Int32 nIdx{ 0 };
556     for (auto& rTemplateDir : maTemplateDirs)
557     {
558         aURL.SetSmartProtocol( INetProtocol::File );
559         aURL.SetURL( aDirs.getToken( 0, C_DELIM, nIdx ) );
560         rTemplateDir = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
561 
562         if ( xExpander.is() )
563         {
564             const sal_Int32 nIndex{ rTemplateDir.indexOf( aPrefix ) };
565             if (nIndex<0)
566                 continue;
567 
568             rTemplateDir = rTemplateDir.replaceAt(nIndex, aPrefix.getLength(), OUString());
569             rTemplateDir = xExpander->expandMacros( rTemplateDir );
570         }
571     }
572 
573     aValue <<= maTemplateDirs;
574 
575     css::uno::Reference< css::util::XPathSettings > xPathSettings =
576         css::util::thePathSettings::get(mxContext);
577 
578     // load internal paths
579     Any aAny = xPathSettings->getPropertyValue( "Template_internal" );
580     aAny >>= maInternalTemplateDirs;
581 
582     for (auto& rInternalTemplateDir : maInternalTemplateDirs)
583     {
584         //expand vnd.sun.star.expand: and remove "..." from them
585         //to normalize into the expected url patterns
586         maRelocator.makeRelocatableURL(rInternalTemplateDir);
587         maRelocator.makeAbsoluteURL(rInternalTemplateDir);
588     }
589 
590     // Store the template dir list
591     setProperty( maRootContent, PROPERTY_DIRLIST, aValue );
592 }
593 
594 
needsUpdate()595 bool SfxDocTplService_Impl::needsUpdate()
596 {
597     bool bNeedsUpdate = true;
598     Any      aValue;
599 
600     // Get the template dir list
601     bool bHasProperty = getProperty( maRootContent, PROPERTY_NEEDSUPDATE, aValue );
602 
603     if ( bHasProperty )
604         aValue >>= bNeedsUpdate;
605 
606     // the old template component also checks this state, but it is initialized from this component
607     // so if this component was already updated the old component does not need such an update
608     ::svt::TemplateFolderCache aTempCache;
609     if ( !bNeedsUpdate )
610         bNeedsUpdate = aTempCache.needsUpdate();
611 
612     if ( bNeedsUpdate )
613         aTempCache.storeState();
614 
615     return bNeedsUpdate;
616 }
617 
618 
setTitleForURL(const OUString & rURL,const OUString & aTitle)619 bool SfxDocTplService_Impl::setTitleForURL( const OUString& rURL, const OUString& aTitle )
620 {
621     if (m_xDocProps.is())
622     {
623         try
624         {
625             m_xDocProps->loadFromMedium(rURL, Sequence<PropertyValue>());
626             m_xDocProps->setTitle(aTitle);
627 
628             uno::Reference< embed::XStorage > xStorage = ::comphelper::OStorageHelper::GetStorageFromURL(
629                     rURL, embed::ElementModes::READWRITE);
630 
631             uno::Sequence<beans::PropertyValue> medium( comphelper::InitPropertySequence({
632                     { "DocumentBaseURL", Any(rURL) },
633                     { "URL", Any(rURL) }
634                 }));
635 
636             m_xDocProps->storeToStorage(xStorage, medium);
637             return true;
638         }
639         catch ( Exception& )
640         {
641         }
642     }
643     return false;
644 }
645 
646 
getTitleFromURL(const OUString & rURL,OUString & aTitle,OUString & aType,bool & bDocHasTitle)647 void SfxDocTplService_Impl::getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, bool& bDocHasTitle )
648 {
649     bDocHasTitle = false;
650 
651     if (m_xDocProps.is())
652     {
653         try
654         {
655             m_xDocProps->loadFromMedium(rURL, Sequence<PropertyValue>());
656             aTitle = m_xDocProps->getTitle();
657         }
658         catch ( Exception& )
659         {
660         }
661     }
662 
663     if ( aType.isEmpty() && mxType.is() )
664     {
665         const OUString aDocType {mxType->queryTypeByURL( rURL )};
666         if ( !aDocType.isEmpty() )
667             try
668             {
669                 uno::Reference< container::XNameAccess > xTypeDetection( mxType, uno::UNO_QUERY_THROW );
670                 SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aDocType ) );
671                 aType = aTypeProps.getUnpackedValueOrDefault(
672                             "MediaType",
673                             OUString() );
674             }
675             catch( uno::Exception& )
676             {}
677     }
678 
679     if ( aTitle.isEmpty() )
680     {
681         INetURLObject aURL( rURL );
682         aURL.CutExtension();
683         aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
684                                INetURLObject::DecodeMechanism::WithCharset );
685     }
686     else
687         bDocHasTitle = true;
688 }
689 
690 
addEntry(Content & rParentFolder,const OUString & rTitle,const OUString & rTargetURL,const OUString & rType)691 bool SfxDocTplService_Impl::addEntry( Content& rParentFolder,
692                                           const OUString& rTitle,
693                                           const OUString& rTargetURL,
694                                           const OUString& rType )
695 {
696     bool bAddedEntry = false;
697 
698     INetURLObject aLinkObj( rParentFolder.getURL() );
699     aLinkObj.insertName( rTitle, false,
700                       INetURLObject::LAST_SEGMENT,
701                       INetURLObject::EncodeMechanism::All );
702     const OUString aLinkURL {aLinkObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
703 
704     Content aLink;
705 
706     if ( ! Content::create( aLinkURL, maCmdEnv, comphelper::getProcessComponentContext(), aLink ) )
707     {
708         Sequence< OUString > aNames(3);
709         aNames[0] = TITLE;
710         aNames[1] = IS_FOLDER;
711         aNames[2] = TARGET_URL;
712 
713         Sequence< Any > aValues(3);
714         aValues[0] <<= rTitle;
715         aValues[1] <<= false;
716         aValues[2] <<= rTargetURL;
717 
718         try
719         {
720             rParentFolder.insertNewContent( TYPE_LINK, aNames, aValues, aLink );
721             setProperty( aLink, PROPERTY_TYPE, makeAny( rType ) );
722             bAddedEntry = true;
723         }
724         catch( Exception& )
725         {}
726     }
727     return bAddedEntry;
728 }
729 
730 
createFolder(const OUString & rNewFolderURL,bool bCreateParent,bool bFsysFolder,Content & rNewFolder)731 bool SfxDocTplService_Impl::createFolder( const OUString& rNewFolderURL,
732                                               bool  bCreateParent,
733                                               bool  bFsysFolder,
734                                               Content   &rNewFolder )
735 {
736     Content         aParent;
737     bool        bCreatedFolder = false;
738     INetURLObject   aParentURL( rNewFolderURL );
739     const OUString aFolderName {aParentURL.getName( INetURLObject::LAST_SEGMENT, true,
740                                                     INetURLObject::DecodeMechanism::WithCharset )};
741 
742     // compute the parent folder url from the new folder url
743     // and remove the final slash, because Content::create doesn't
744     // like it
745     aParentURL.removeSegment();
746     if ( aParentURL.getSegmentCount() >= 1 )
747         aParentURL.removeFinalSlash();
748 
749     // if the parent exists, we can continue with the creation of the
750     // new folder, we have to create the parent otherwise ( as long as
751     // bCreateParent is set to true )
752     if ( Content::create( aParentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), maCmdEnv, comphelper::getProcessComponentContext(), aParent ) )
753     {
754         try
755         {
756             Sequence< OUString > aNames(2);
757             aNames[0] = TITLE;
758             aNames[1] = IS_FOLDER;
759 
760             Sequence< Any > aValues(2);
761             aValues[0] <<= aFolderName;
762             aValues[1] <<= true;
763 
764             OUString aType;
765 
766             if ( bFsysFolder )
767                 aType = TYPE_FSYS_FOLDER;
768             else
769                 aType = TYPE_FOLDER;
770 
771             aParent.insertNewContent( aType, aNames, aValues, rNewFolder );
772             bCreatedFolder = true;
773         }
774         catch( Exception const & )
775         {
776             TOOLS_WARN_EXCEPTION( "sfx.doc", "createFolder(): Could not create new folder" );
777         }
778     }
779     else if ( bCreateParent )
780     {
781         // if the parent doesn't exists and bCreateParent is set to true,
782         // we try to create the parent and if this was successful, we
783         // try to create the new folder again ( but this time, we set
784         // bCreateParent to false to avoid endless recursions )
785         if ( ( aParentURL.getSegmentCount() >= 1 ) &&
786                createFolder( aParentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), bCreateParent, bFsysFolder, aParent ) )
787         {
788             bCreatedFolder = createFolder( rNewFolderURL, false, bFsysFolder, rNewFolder );
789         }
790     }
791 
792     return bCreatedFolder;
793 }
794 
795 
CreateNewUniqueFolderWithPrefix(const OUString & aPath,const OUString & aPrefix,OUString & aNewFolderName,OUString & aNewFolderURL,Content & aNewFolder)796 bool SfxDocTplService_Impl::CreateNewUniqueFolderWithPrefix( const OUString& aPath,
797                                                                 const OUString& aPrefix,
798                                                                 OUString& aNewFolderName,
799                                                                 OUString& aNewFolderURL,
800                                                                 Content& aNewFolder )
801 {
802     bool bCreated = false;
803     INetURLObject aDirPath( aPath );
804 
805     Content aParent;
806     uno::Reference< XCommandEnvironment > aQuietEnv;
807     if ( Content::create( aDirPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aQuietEnv, comphelper::getProcessComponentContext(), aParent ) )
808     {
809         for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ )
810         {
811             OUString aTryName = aPrefix;
812             if ( nInd )
813                 aTryName += OUString::number( nInd );
814 
815             try
816             {
817                 Sequence< OUString > aNames(2);
818                 aNames[0] = TITLE;
819                 aNames[1] = IS_FOLDER;
820 
821                 Sequence< Any > aValues(2);
822                 aValues[0] <<= aTryName;
823                 aValues[1] <<= true;
824 
825                 bCreated = aParent.insertNewContent( TYPE_FSYS_FOLDER, aNames, aValues, aNewFolder );
826             }
827             catch( ucb::NameClashException& )
828             {
829                 // if there is already an element, retry
830             }
831             catch( Exception& )
832             {
833                 INetURLObject aObjPath( aDirPath );
834                 aObjPath.insertName( aTryName, false,
835                       INetURLObject::LAST_SEGMENT,
836                       INetURLObject::EncodeMechanism::All );
837                 // if there is already an element, retry
838                 // if there was another error, do not try any more
839                 if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
840                     break;
841             }
842 
843             if ( bCreated )
844             {
845                 aNewFolderName = aTryName;
846                 aNewFolderURL = aNewFolder.get()->getIdentifier()->getContentIdentifier();
847                 break;
848             }
849         }
850     }
851 
852     return bCreated;
853 }
854 
855 
CreateNewUniqueFileWithPrefix(const OUString & aPath,const OUString & aPrefix,const OUString & aExt)856 OUString SfxDocTplService_Impl::CreateNewUniqueFileWithPrefix( const OUString& aPath,
857                                                                         const OUString& aPrefix,
858                                                                         const OUString& aExt )
859 {
860     OUString aNewFileURL;
861     INetURLObject aDirPath( aPath );
862 
863     Content aParent;
864 
865     uno::Reference< XCommandEnvironment > aQuietEnv;
866     if ( Content::create( aDirPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aQuietEnv, comphelper::getProcessComponentContext(), aParent ) )
867        {
868         for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ )
869         {
870             Content aNewFile;
871             bool bCreated = false;
872             OUString aTryName = aPrefix;
873             if ( nInd )
874                 aTryName += OUString::number( nInd );
875             if ( aExt.toChar() != '.' )
876                 aTryName += ".";
877             aTryName += aExt;
878 
879             try
880             {
881                 Sequence< OUString > aNames(2);
882                 aNames[0] = TITLE;
883                 aNames[1] = IS_DOCUMENT;
884 
885                 Sequence< Any > aValues(2);
886                 aValues[0] <<= aTryName;
887                 aValues[1] <<= true;
888 
889                 bCreated = aParent.insertNewContent( TYPE_FSYS_FILE, aNames, aValues, aNewFile );
890             }
891             catch( ucb::NameClashException& )
892             {
893                 // if there is already an element, retry
894             }
895             catch( Exception& )
896             {
897                 INetURLObject aObjPath( aPath );
898                 aObjPath.insertName( aTryName, false,
899                       INetURLObject::LAST_SEGMENT,
900                       INetURLObject::EncodeMechanism::All );
901                 // if there is already an element, retry
902                 // if there was another error, do not try any more
903                 if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
904                     break;
905             }
906 
907             if ( bCreated )
908             {
909                 aNewFileURL = aNewFile.get()->getIdentifier()->getContentIdentifier();
910                 break;
911             }
912         }
913     }
914 
915     return aNewFileURL;
916 }
917 
918 
removeContent(Content & rContent)919 bool SfxDocTplService_Impl::removeContent( Content& rContent )
920 {
921     bool bRemoved = false;
922     try
923     {
924         Any aArg = makeAny( true );
925 
926         rContent.executeCommand( COMMAND_DELETE, aArg );
927         bRemoved = true;
928     }
929     catch ( RuntimeException& ) {}
930     catch ( Exception& ) {}
931 
932     return bRemoved;
933 }
934 
935 
removeContent(const OUString & rContentURL)936 bool SfxDocTplService_Impl::removeContent( const OUString& rContentURL )
937 {
938     Content aContent;
939 
940     if ( Content::create( rContentURL, maCmdEnv, comphelper::getProcessComponentContext(), aContent ) )
941         return removeContent( aContent );
942     return false;
943 }
944 
945 
setProperty(Content & rContent,const OUString & rPropName,const Any & rPropValue)946 bool SfxDocTplService_Impl::setProperty( Content& rContent,
947                                              const OUString& rPropName,
948                                              const Any& rPropValue )
949 {
950     bool bPropertySet = false;
951 
952     // Store the property
953     try
954     {
955         Any aPropValue( rPropValue );
956         uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
957 
958         // check, whether or not the property exists, create it, when not
959         if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
960         {
961             uno::Reference< XPropertyContainer > xProperties( rContent.get(), UNO_QUERY );
962             if ( xProperties.is() )
963             {
964                 try
965                 {
966                     xProperties->addProperty( rPropName, PropertyAttribute::MAYBEVOID, rPropValue );
967                 }
968                 catch( PropertyExistException& ) {}
969                 catch( IllegalTypeException& ) {
970                     SAL_WARN( "sfx.doc", "IllegalTypeException" );
971                 }
972                 catch( IllegalArgumentException& ) {
973                     SAL_WARN( "sfx.doc", "IllegalArgumentException" );
974                 }
975             }
976         }
977 
978         // To ensure a reloctable office installation, the path to the
979         // office installation directory must never be stored directly.
980         if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
981         {
982             OUString aValue;
983             if ( rPropValue >>= aValue )
984             {
985                 maRelocator.makeRelocatableURL( aValue );
986                 aPropValue <<= aValue;
987             }
988             else
989             {
990                 Sequence< OUString > aValues;
991                 if ( rPropValue >>= aValues )
992                 {
993                     for ( auto& rValue : aValues )
994                     {
995                         maRelocator.makeRelocatableURL( rValue );
996                     }
997                     aPropValue <<= aValues;
998                 }
999                 else
1000                 {
1001                     OSL_FAIL( "Unsupported property value type" );
1002                 }
1003             }
1004         }
1005 
1006         // now set the property
1007 
1008         rContent.setPropertyValue( rPropName, aPropValue );
1009         bPropertySet = true;
1010     }
1011     catch ( RuntimeException& ) {}
1012     catch ( Exception& ) {}
1013 
1014     return bPropertySet;
1015 }
1016 
1017 
getProperty(Content & rContent,const OUString & rPropName,Any & rPropValue)1018 bool SfxDocTplService_Impl::getProperty(Content& rContent, const OUString& rPropName, Any& rPropValue)
1019 {
1020     bool bGotProperty = false;
1021 
1022     // Get the property
1023     try
1024     {
1025         uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();
1026 
1027         // check, whether or not the property exists
1028         if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
1029         {
1030             return false;
1031         }
1032 
1033         // now get the property
1034 
1035         rPropValue = rContent.getPropertyValue( rPropName );
1036 
1037         // To ensure a reloctable office installation, the path to the
1038         // office installation directory must never be stored directly.
1039         if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
1040         {
1041             OUString aValue;
1042             if ( rPropValue >>= aValue )
1043             {
1044                 maRelocator.makeAbsoluteURL( aValue );
1045                 rPropValue <<= aValue;
1046             }
1047             else
1048             {
1049                 Sequence< OUString > aValues;
1050                 if ( rPropValue >>= aValues )
1051                 {
1052                     for ( auto& rValue : aValues )
1053                     {
1054                         maRelocator.makeAbsoluteURL( rValue );
1055                     }
1056                     rPropValue <<= aValues;
1057                 }
1058                 else
1059                 {
1060                     OSL_FAIL( "Unsupported property value type" );
1061                 }
1062             }
1063         }
1064 
1065         bGotProperty = true;
1066     }
1067     catch ( RuntimeException& ) {}
1068     catch ( Exception& ) {}
1069 
1070     return bGotProperty;
1071 }
1072 
SfxDocTplService_Impl(const uno::Reference<XComponentContext> & xContext)1073 SfxDocTplService_Impl::SfxDocTplService_Impl( const uno::Reference< XComponentContext > & xContext )
1074     : maRelocator(xContext)
1075 {
1076     mxContext       = xContext;
1077     mbIsInitialized = false;
1078     mbLocaleSet     = false;
1079 }
1080 
1081 
~SfxDocTplService_Impl()1082 SfxDocTplService_Impl::~SfxDocTplService_Impl()
1083 {
1084     ::osl::MutexGuard aGuard( maMutex );
1085     maNames.clear();
1086 }
1087 
1088 
getLocale()1089 lang::Locale SfxDocTplService_Impl::getLocale()
1090 {
1091     ::osl::MutexGuard aGuard( maMutex );
1092 
1093     if ( !mbLocaleSet )
1094         getDefaultLocale();
1095 
1096     return maLocale;
1097 }
1098 
1099 
setLocale(const lang::Locale & rLocale)1100 void SfxDocTplService_Impl::setLocale( const lang::Locale &rLocale )
1101 {
1102     ::osl::MutexGuard aGuard( maMutex );
1103 
1104     if ( mbLocaleSet && (
1105          ( maLocale.Language != rLocale.Language ) ||
1106          ( maLocale.Country  != rLocale.Country  ) ||
1107          ( maLocale.Variant  != rLocale.Variant  ) ) )
1108         mbIsInitialized = false;
1109 
1110     maLocale    = rLocale;
1111     mbLocaleSet = true;
1112 }
1113 
1114 
update()1115 void SfxDocTplService_Impl::update()
1116 {
1117     ::osl::MutexGuard aGuard( maMutex );
1118 
1119     doUpdate();
1120 }
1121 
1122 
doUpdate()1123 void SfxDocTplService_Impl::doUpdate()
1124 {
1125     ::osl::MutexGuard aGuard( maMutex );
1126 
1127     const OUString aPropName( PROPERTY_NEEDSUPDATE );
1128     Any      aValue;
1129 
1130     aValue <<= true;
1131     setProperty( maRootContent, aPropName, aValue );
1132 
1133     GroupList_Impl  aGroupList;
1134 
1135     // get the entries from the hierarchy
1136     createFromContent( aGroupList, maRootContent, true, false );
1137 
1138     // get the entries from the template directories
1139     sal_Int32   nCountDir = maTemplateDirs.getLength();
1140     OUString*   pDirs = maTemplateDirs.getArray();
1141     Content     aDirContent;
1142 
1143     // the last directory in the list must be writable
1144     bool bWriteableDirectory = true;
1145 
1146     // the target folder might not exist, for this reason no interaction handler should be used
1147     uno::Reference< XCommandEnvironment > aQuietEnv;
1148 
1149     while ( nCountDir )
1150     {
1151         nCountDir--;
1152         if ( Content::create( pDirs[ nCountDir ], aQuietEnv, comphelper::getProcessComponentContext(), aDirContent ) )
1153         {
1154             createFromContent( aGroupList, aDirContent, false, bWriteableDirectory );
1155         }
1156 
1157         bWriteableDirectory = false;
1158     }
1159 
1160     // now check the list
1161     for(std::unique_ptr<GroupData_Impl>& pGroup : aGroupList)
1162     {
1163         if ( pGroup->getInUse() )
1164         {
1165             if ( pGroup->getInHierarchy() )
1166             {
1167                 Content aGroup;
1168                 if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1169                     setProperty( aGroup,
1170                                  TARGET_DIR_URL,
1171                                  makeAny( pGroup->getTargetURL() ) );
1172 
1173                 size_t nCount = pGroup->count();
1174                 for ( size_t i=0; i<nCount; i++ )
1175                 {
1176                     DocTemplates_EntryData_Impl *pData = pGroup->getEntry( i );
1177                     if ( ! pData->getInUse() )
1178                     {
1179                         if ( pData->getInHierarchy() )
1180                             removeFromHierarchy( pData ); // delete entry in hierarchy
1181                         else
1182                             addToHierarchy( pGroup.get(), pData ); // add entry to hierarchy
1183                     }
1184                     else if ( pData->getUpdateType() ||
1185                               pData->getUpdateLink() )
1186                     {
1187                         updateData( pData );
1188                     }
1189                 }
1190             }
1191             else
1192             {
1193                 addGroupToHierarchy( pGroup.get() ); // add group to hierarchy
1194             }
1195         }
1196         else
1197             removeFromHierarchy( pGroup.get() ); // delete group from hierarchy
1198     }
1199     aGroupList.clear();
1200 
1201     aValue <<= false;
1202     setProperty( maRootContent, aPropName, aValue );
1203 }
1204 
1205 
ReadUINamesForTemplateDir_Impl(const OUString & aUserPath)1206 std::vector< beans::StringPair > SfxDocTplService_Impl::ReadUINamesForTemplateDir_Impl( const OUString& aUserPath )
1207 {
1208     INetURLObject aLocObj( aUserPath );
1209     aLocObj.insertName( "groupuinames.xml", false,
1210                       INetURLObject::LAST_SEGMENT,
1211                       INetURLObject::EncodeMechanism::All );
1212     Content aLocContent;
1213 
1214     // TODO/LATER: Use hashmap in future
1215     std::vector< beans::StringPair > aUINames;
1216     if ( Content::create( aLocObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference < ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext(), aLocContent ) )
1217     {
1218         try
1219         {
1220             uno::Reference< io::XInputStream > xLocStream = aLocContent.openStream();
1221             if ( xLocStream.is() )
1222                 aUINames = DocTemplLocaleHelper::ReadGroupLocalizationSequence( xLocStream, mxContext );
1223         }
1224         catch( uno::Exception& )
1225         {}
1226     }
1227 
1228     return aUINames;
1229 }
1230 
1231 
UpdateUINamesForTemplateDir_Impl(const OUString & aUserPath,const OUString & aGroupName,const OUString & aNewFolderName)1232 bool SfxDocTplService_Impl::UpdateUINamesForTemplateDir_Impl( const OUString& aUserPath,
1233                                                                   const OUString& aGroupName,
1234                                                                   const OUString& aNewFolderName )
1235 {
1236     std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
1237     sal_Int32 nLen = aUINames.size();
1238 
1239     // it is possible that the name is used already, but it should be checked before
1240     for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
1241         if ( aUINames[nInd].First == aNewFolderName )
1242             return false;
1243 
1244     aUINames.resize( ++nLen );
1245     aUINames[nLen-1].First = aNewFolderName;
1246     aUINames[nLen-1].Second = aGroupName;
1247 
1248     return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames );
1249 }
1250 
1251 
ReplaceUINamesForTemplateDir_Impl(const OUString & aUserPath,const OUString & aDefaultFsysGroupName,const OUString & aOldGroupName,const OUString & aNewGroupName)1252 bool SfxDocTplService_Impl::ReplaceUINamesForTemplateDir_Impl( const OUString& aUserPath,
1253                                                                   const OUString& aDefaultFsysGroupName,
1254                                                                   const OUString& aOldGroupName,
1255                                                                   const OUString& aNewGroupName )
1256 {
1257     std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
1258     sal_Int32 nLen = aUINames.size();
1259 
1260     bool bChanged = false;
1261     for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
1262         if ( aUINames[nInd].Second == aOldGroupName )
1263         {
1264             aUINames[nInd].Second = aNewGroupName;
1265             bChanged = true;
1266         }
1267 
1268     if ( !bChanged )
1269     {
1270         aUINames.resize( ++nLen );
1271         aUINames[nLen-1].First = aDefaultFsysGroupName;
1272         aUINames[nLen-1].Second = aNewGroupName;
1273     }
1274     return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames );
1275 }
1276 
1277 
RemoveUINamesForTemplateDir_Impl(const OUString & aUserPath,const OUString & aGroupName)1278 void SfxDocTplService_Impl::RemoveUINamesForTemplateDir_Impl( const OUString& aUserPath,
1279                                                                   const OUString& aGroupName )
1280 {
1281     std::vector< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
1282     sal_Int32 nLen = aUINames.size();
1283     std::vector< beans::StringPair > aNewUINames( nLen );
1284     sal_Int32 nNewLen = 0;
1285 
1286     bool bChanged = false;
1287     for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
1288         if ( aUINames[nInd].Second == aGroupName )
1289             bChanged = true;
1290         else
1291         {
1292             nNewLen++;
1293             aNewUINames[nNewLen-1].First = aUINames[nInd].First;
1294             aNewUINames[nNewLen-1].Second = aUINames[nInd].Second;
1295         }
1296 
1297     aNewUINames.resize( nNewLen );
1298 
1299     if (bChanged)
1300         WriteUINamesForTemplateDir_Impl( aUserPath, aNewUINames );
1301 }
1302 
1303 
WriteUINamesForTemplateDir_Impl(const OUString & aUserPath,const std::vector<beans::StringPair> & aUINames)1304 bool SfxDocTplService_Impl::WriteUINamesForTemplateDir_Impl( const OUString& aUserPath,
1305                                                              const std::vector< beans::StringPair >& aUINames )
1306 {
1307     bool bResult = false;
1308     try {
1309         uno::Reference< beans::XPropertySet > xTempFile(
1310                 io::TempFile::create(mxContext),
1311                 uno::UNO_QUERY_THROW );
1312 
1313         OUString aTempURL;
1314         uno::Any aUrl = xTempFile->getPropertyValue("Uri");
1315         aUrl >>= aTempURL;
1316 
1317         uno::Reference< io::XStream > xStream( xTempFile, uno::UNO_QUERY_THROW );
1318         uno::Reference< io::XOutputStream > xOutStream = xStream->getOutputStream();
1319         if ( !xOutStream.is() )
1320             throw uno::RuntimeException();
1321 
1322         DocTemplLocaleHelper::WriteGroupLocalizationSequence( xOutStream, aUINames, mxContext);
1323         try {
1324             // the SAX writer might close the stream
1325             xOutStream->closeOutput();
1326         } catch( uno::Exception& )
1327         {}
1328 
1329         Content aTargetContent( aUserPath, maCmdEnv, comphelper::getProcessComponentContext() );
1330         Content aSourceContent( aTempURL, maCmdEnv, comphelper::getProcessComponentContext() );
1331         aTargetContent.transferContent( aSourceContent,
1332                                         InsertOperation::Copy,
1333                                         "groupuinames.xml",
1334                                         ucb::NameClash::OVERWRITE,
1335                                         "text/xml" );
1336         bResult = true;
1337     }
1338     catch ( uno::Exception& )
1339     {
1340     }
1341 
1342     return bResult;
1343 }
1344 
1345 
CreateNewGroupFsys(const OUString & rGroupName,Content & aGroup)1346 OUString SfxDocTplService_Impl::CreateNewGroupFsys( const OUString& rGroupName, Content& aGroup )
1347 {
1348     OUString aResultURL;
1349 
1350     if ( maTemplateDirs.hasElements() )
1351     {
1352         OUString aTargetPath = maTemplateDirs[ maTemplateDirs.getLength() - 1 ];
1353 
1354         // create a new folder with the given name
1355         Content aNewFolder;
1356         OUString aNewFolderName;
1357 
1358         // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1359         if ( !CreateNewUniqueFolderWithPrefix( aTargetPath,
1360                                                 rGroupName,
1361                                                 aNewFolderName,
1362                                                 aResultURL,
1363                                                 aNewFolder )
1364           && !CreateNewUniqueFolderWithPrefix( aTargetPath,
1365                                                 "UserGroup",
1366                                                 aNewFolderName,
1367                                                 aResultURL,
1368                                                 aNewFolder ) )
1369 
1370             return OUString();
1371 
1372         if ( !UpdateUINamesForTemplateDir_Impl( aTargetPath, rGroupName, aNewFolderName ) )
1373         {
1374             // we could not create the groupuinames for the folder, so we delete the group in
1375             // the folder and return
1376             removeContent( aNewFolder );
1377             return OUString();
1378         }
1379 
1380         // Now set the target url for this group and we are done
1381         Any aValue = makeAny( aResultURL );
1382 
1383         if ( ! setProperty( aGroup, TARGET_DIR_URL, aValue ) )
1384         {
1385             removeContent( aNewFolder );
1386             return OUString();
1387         }
1388     }
1389 
1390     return aResultURL;
1391 }
1392 
1393 
addGroup(const OUString & rGroupName)1394 bool SfxDocTplService_Impl::addGroup( const OUString& rGroupName )
1395 {
1396     ::osl::MutexGuard aGuard( maMutex );
1397 
1398     // Check, whether or not there is a group with this name
1399     Content      aNewGroup;
1400     OUString        aNewGroupURL;
1401     INetURLObject   aNewGroupObj( maRootURL );
1402 
1403     aNewGroupObj.insertName( rGroupName, false,
1404                       INetURLObject::LAST_SEGMENT,
1405                       INetURLObject::EncodeMechanism::All );
1406 
1407     aNewGroupURL = aNewGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1408 
1409     if ( Content::create( aNewGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aNewGroup ) ||
1410          ! createFolder( aNewGroupURL, false, false, aNewGroup ) )
1411     {
1412         // if there already was a group with this name or the new group
1413         // could not be created, we return here
1414         return false;
1415     }
1416 
1417     // Get the user template path entry ( new group will always
1418     // be added in the user template path )
1419     sal_Int32   nIndex;
1420     OUString    aUserPath;
1421 
1422     nIndex = maTemplateDirs.getLength();
1423     if ( nIndex )
1424         nIndex--;
1425     else
1426         return false;   // We don't know where to add the group
1427 
1428     aUserPath = maTemplateDirs[ nIndex ];
1429 
1430     // create a new folder with the given name
1431     Content      aNewFolder;
1432     OUString        aNewFolderName;
1433     OUString        aNewFolderURL;
1434 
1435     // the Fsys name instead of GroupName should be used, the groupuinames must be added also
1436     if ( !CreateNewUniqueFolderWithPrefix( aUserPath,
1437                                             rGroupName,
1438                                             aNewFolderName,
1439                                             aNewFolderURL,
1440                                             aNewFolder )
1441       && !CreateNewUniqueFolderWithPrefix( aUserPath,
1442                                             "UserGroup",
1443                                             aNewFolderName,
1444                                             aNewFolderURL,
1445                                             aNewFolder ) )
1446     {
1447         // we could not create the folder, so we delete the group in the
1448         // hierarchy and return
1449         removeContent( aNewGroup );
1450         return false;
1451     }
1452 
1453     if ( !UpdateUINamesForTemplateDir_Impl( aUserPath, rGroupName, aNewFolderName ) )
1454     {
1455         // we could not create the groupuinames for the folder, so we delete the group in the
1456         // hierarchy, the folder and return
1457         removeContent( aNewGroup );
1458         removeContent( aNewFolder );
1459         return false;
1460     }
1461 
1462     // Now set the target url for this group and we are done
1463     Any aValue = makeAny( aNewFolderURL );
1464 
1465     if ( ! setProperty( aNewGroup, TARGET_DIR_URL, aValue ) )
1466     {
1467         removeContent( aNewGroup );
1468         removeContent( aNewFolder );
1469         return false;
1470     }
1471 
1472     return true;
1473 }
1474 
1475 
removeGroup(const OUString & rGroupName)1476 bool SfxDocTplService_Impl::removeGroup( const OUString& rGroupName )
1477 {
1478     // remove all the elements that have the prefix aTargetURL
1479     // if the group does not have other elements remove it
1480 
1481     ::osl::MutexGuard aGuard( maMutex );
1482 
1483     bool bResult = false;
1484 
1485     // create the group url
1486     INetURLObject aGroupObj( maRootURL );
1487     aGroupObj.insertName( rGroupName, false,
1488                       INetURLObject::LAST_SEGMENT,
1489                       INetURLObject::EncodeMechanism::All );
1490 
1491     // Get the target url
1492     Content  aGroup;
1493     const OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1494 
1495     if ( Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1496     {
1497         const OUString aPropName( TARGET_DIR_URL  );
1498         Any      aValue;
1499 
1500         OUString    aGroupTargetURL;
1501         if ( getProperty( aGroup, aPropName, aValue ) )
1502             aValue >>= aGroupTargetURL;
1503 
1504         if ( aGroupTargetURL.isEmpty() )
1505             return false; // nothing is allowed to be removed
1506 
1507         if ( !maTemplateDirs.hasElements() )
1508             return false;
1509 
1510         // check that the fs location is in writable folder and this is not a "My templates" folder
1511         INetURLObject aGroupParentFolder( aGroupTargetURL );
1512         if (!aGroupParentFolder.removeSegment())
1513             return false;
1514 
1515         OUString aGeneralTempPath = findParentTemplateDir(
1516             aGroupParentFolder.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1517 
1518         if (aGeneralTempPath.isEmpty())
1519             return false;
1520 
1521         // now get the content of the Group
1522         uno::Reference< XResultSet > xResultSet;
1523         Sequence< OUString > aProps { TARGET_URL };
1524 
1525         try
1526         {
1527             xResultSet = aGroup.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY );
1528 
1529             if ( xResultSet.is() )
1530             {
1531                 bool bHasNonRemovable = false;
1532                 bool bHasShared = false;
1533 
1534                 uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
1535                 uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );
1536 
1537                 while ( xResultSet->next() )
1538                 {
1539                     OUString aTemplTargetURL( xRow->getString( 1 ) );
1540                     OUString aHierURL = xContentAccess->queryContentIdentifierString();
1541 
1542                     if ( ::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, aTemplTargetURL ) )
1543                     {
1544                         // this is a user template, and it can be removed
1545                         if ( removeContent( aTemplTargetURL ) )
1546                             removeContent( aHierURL );
1547                         else
1548                             bHasNonRemovable = true;
1549                     }
1550                     else
1551                         bHasShared = true;
1552                 }
1553 
1554                 if ( !bHasNonRemovable && !bHasShared )
1555                 {
1556                     if ( removeContent( aGroupTargetURL )
1557                       || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) )
1558                     {
1559                         removeContent( aGroupURL );
1560                         RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName );
1561                         bResult = true; // the operation is successful only if the whole group is removed
1562                     }
1563                 }
1564                 else if ( !bHasNonRemovable )
1565                 {
1566                     if ( removeContent( aGroupTargetURL )
1567                       || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) )
1568                     {
1569                         RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName );
1570                         setProperty( aGroup, aPropName, uno::makeAny( OUString() ) );
1571                     }
1572                 }
1573             }
1574         }
1575         catch ( Exception& ) {}
1576     }
1577 
1578     return bResult;
1579 }
1580 
1581 
renameGroup(const OUString & rOldName,const OUString & rNewName)1582 bool SfxDocTplService_Impl::renameGroup( const OUString& rOldName,
1583                                              const OUString& rNewName )
1584 {
1585     ::osl::MutexGuard aGuard( maMutex );
1586 
1587     // create the group url
1588     Content         aGroup;
1589     INetURLObject   aGroupObj( maRootURL );
1590     aGroupObj.insertName( rNewName, false,
1591                           INetURLObject::LAST_SEGMENT,
1592                           INetURLObject::EncodeMechanism::All );
1593     OUString        aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1594 
1595     // Check, if there is a group with the new name, return false
1596     // if there is one.
1597     if ( Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1598         return false;
1599 
1600     aGroupObj.removeSegment();
1601     aGroupObj.insertName( rOldName, false,
1602                       INetURLObject::LAST_SEGMENT,
1603                       INetURLObject::EncodeMechanism::All );
1604     aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1605 
1606     // When there is no group with the old name, we can't rename it
1607     if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1608         return false;
1609 
1610     OUString aGroupTargetURL;
1611     // there is no need to check whether target dir url is in target path, since if the target path is changed
1612     // the target dir url should be already generated new
1613     Any      aValue;
1614     if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
1615         aValue >>= aGroupTargetURL;
1616 
1617     if ( aGroupTargetURL.isEmpty() )
1618         return false;
1619 
1620     if ( !maTemplateDirs.hasElements() )
1621         return false;
1622 
1623     // check that the fs location is in writable folder and this is not a "My templates" folder
1624     INetURLObject aGroupParentFolder( aGroupTargetURL );
1625     if (!aGroupParentFolder.removeSegment() ||
1626         isInternalTemplateDir(aGroupParentFolder.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
1627     {
1628         return false;
1629     }
1630 
1631     // check that the group can be renamed ( all the contents must be in target location )
1632     bool bCanBeRenamed = false;
1633     try
1634     {
1635         uno::Reference< XResultSet > xResultSet;
1636         Sequence< OUString > aProps { TARGET_URL };
1637         xResultSet = aGroup.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY );
1638 
1639         if ( xResultSet.is() )
1640         {
1641             uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
1642             uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );
1643 
1644             while ( xResultSet->next() )
1645             {
1646                 if ( !::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, xRow->getString( 1 ) ) )
1647                     throw uno::Exception("not sub path", nullptr);
1648             }
1649 
1650             bCanBeRenamed = true;
1651         }
1652     }
1653     catch ( Exception& ) {}
1654 
1655     if ( bCanBeRenamed )
1656     {
1657         INetURLObject aGroupTargetObj( aGroupTargetURL );
1658         const OUString aFsysName = aGroupTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
1659 
1660         if ( aGroupTargetObj.removeSegment()
1661           && ReplaceUINamesForTemplateDir_Impl( aGroupTargetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1662                                                   aFsysName,
1663                                                 rOldName,
1664                                                 rNewName ) )
1665         {
1666             // rename the group in the hierarchy
1667             Any aTitleValue;
1668             aTitleValue <<= rNewName;
1669 
1670             return setProperty( aGroup, TITLE, aTitleValue );
1671         }
1672     }
1673 
1674     return false;
1675 }
1676 
1677 
storeTemplate(const OUString & rGroupName,const OUString & rTemplateName,const uno::Reference<frame::XStorable> & rStorable)1678 bool SfxDocTplService_Impl::storeTemplate( const OUString& rGroupName,
1679                                                const OUString& rTemplateName,
1680                                                const uno::Reference< frame::XStorable >& rStorable )
1681 {
1682     ::osl::MutexGuard aGuard( maMutex );
1683 
1684     // Check, whether or not there is a group with this name
1685     // Return false, if there is no group with the given name
1686     Content         aGroup, aTemplate, aTargetGroup, aTemplateToRemove;
1687     INetURLObject   aGroupObj( maRootURL );
1688     bool        bRemoveOldTemplateContent = false;
1689 
1690     aGroupObj.insertName( rGroupName, false,
1691                       INetURLObject::LAST_SEGMENT,
1692                       INetURLObject::EncodeMechanism::All );
1693     const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
1694 
1695     if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1696         return false;
1697 
1698     OUString aGroupTargetURL;
1699     Any      aValue;
1700     if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
1701         aValue >>= aGroupTargetURL;
1702 
1703 
1704     // Check, if there's a template with the given name in this group
1705     // the target template should be overwritten if it is imported by user
1706     // in case the template is installed by office installation of by an add-in
1707     // it can not be replaced
1708     aGroupObj.insertName( rTemplateName, false,
1709                       INetURLObject::LAST_SEGMENT,
1710                       INetURLObject::EncodeMechanism::All );
1711     const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
1712 
1713     OUString aTemplateToRemoveTargetURL;
1714 
1715     if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplateToRemove ) )
1716     {
1717         bRemoveOldTemplateContent = true;
1718         if ( getProperty( aTemplateToRemove, TARGET_URL, aValue ) )
1719             aValue >>= aTemplateToRemoveTargetURL;
1720 
1721         if ( aGroupTargetURL.isEmpty() || !maTemplateDirs.hasElements()
1722           || (!aTemplateToRemoveTargetURL.isEmpty() && isInternalTemplateDir(aTemplateToRemoveTargetURL)) )
1723             return false; // it is not allowed to remove the template
1724     }
1725 
1726     try
1727     {
1728         uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1729 
1730         // get document service name
1731         uno::Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xContext) );
1732         const OUString sDocServiceName {xModuleManager->identify( uno::Reference< uno::XInterface >( rStorable, uno::UNO_QUERY ) )};
1733         if ( sDocServiceName.isEmpty() )
1734             throw uno::RuntimeException();
1735 
1736         // get the actual filter name
1737         uno::Reference< lang::XMultiServiceFactory > xConfigProvider =
1738                 configuration::theDefaultProvider::get( xContext );
1739 
1740         uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1741         {
1742             {"nodepath", uno::Any(OUString( "/org.openoffice.Setup/Office/Factories/" ))}
1743         }));
1744         uno::Reference< container::XNameAccess > xSOFConfig(
1745             xConfigProvider->createInstanceWithArguments(
1746                                     "com.sun.star.configuration.ConfigurationAccess",
1747                                     aArgs ),
1748             uno::UNO_QUERY_THROW );
1749 
1750         uno::Reference< container::XNameAccess > xApplConfig;
1751         xSOFConfig->getByName( sDocServiceName ) >>= xApplConfig;
1752         if ( !xApplConfig.is() )
1753             throw uno::RuntimeException();
1754 
1755         OUString aFilterName;
1756         xApplConfig->getByName("ooSetupFactoryActualTemplateFilter") >>= aFilterName;
1757         if ( aFilterName.isEmpty() )
1758             throw uno::RuntimeException();
1759 
1760         // find the related type name
1761         uno::Reference< container::XNameAccess > xFilterFactory(
1762             mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", mxContext),
1763             uno::UNO_QUERY_THROW );
1764 
1765         uno::Sequence< beans::PropertyValue > aFilterData;
1766         xFilterFactory->getByName( aFilterName ) >>= aFilterData;
1767         OUString aTypeName;
1768         for ( const auto& rProp : std::as_const(aFilterData) )
1769             if ( rProp.Name == "Type" )
1770                 rProp.Value >>= aTypeName;
1771 
1772         if ( aTypeName.isEmpty() )
1773             throw uno::RuntimeException();
1774 
1775         // find the mediatype and extension
1776         uno::Reference< container::XNameAccess > xTypeDetection =
1777             mxType.is() ?
1778                 uno::Reference< container::XNameAccess >( mxType, uno::UNO_QUERY_THROW ) :
1779                 uno::Reference< container::XNameAccess >(
1780                     mxContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", mxContext),
1781                     uno::UNO_QUERY_THROW );
1782 
1783         SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aTypeName ) );
1784         uno::Sequence< OUString > aAllExt =
1785             aTypeProps.getUnpackedValueOrDefault("Extensions", Sequence< OUString >() );
1786         if ( !aAllExt.hasElements() )
1787             throw uno::RuntimeException();
1788 
1789         const OUString aMediaType {aTypeProps.getUnpackedValueOrDefault("MediaType", OUString() )};
1790         const OUString aExt {aAllExt[0]};
1791 
1792         if ( aMediaType.isEmpty() || aExt.isEmpty() )
1793             throw uno::RuntimeException();
1794 
1795         // construct destination url
1796         if ( aGroupTargetURL.isEmpty() )
1797         {
1798             aGroupTargetURL = CreateNewGroupFsys( rGroupName, aGroup );
1799 
1800             if ( aGroupTargetURL.isEmpty() )
1801                 throw uno::RuntimeException();
1802         }
1803 
1804         OUString aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, rTemplateName, aExt );
1805         if ( aNewTemplateTargetURL.isEmpty() )
1806         {
1807             aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, "UserTemplate", aExt );
1808 
1809             if ( aNewTemplateTargetURL.isEmpty() )
1810                 throw uno::RuntimeException();
1811         }
1812 
1813         // store template
1814         uno::Sequence< PropertyValue > aStoreArgs( 2 );
1815         aStoreArgs[0].Name = "FilterName";
1816         aStoreArgs[0].Value <<= aFilterName;
1817         aStoreArgs[1].Name = "DocumentTitle";
1818         aStoreArgs[1].Value <<= rTemplateName;
1819 
1820         if( !::utl::UCBContentHelper::EqualURLs( aNewTemplateTargetURL, rStorable->getLocation() ))
1821             rStorable->storeToURL( aNewTemplateTargetURL, aStoreArgs );
1822         else
1823             rStorable->store();
1824 
1825         // the storing was successful, now the old template with the same name can be removed if it existed
1826         if ( !aTemplateToRemoveTargetURL.isEmpty() )
1827         {
1828             removeContent( aTemplateToRemoveTargetURL );
1829 
1830             /*
1831              * pb: #i79496#
1832              * if the old template was the standard template
1833              * it is necessary to change the standard template with the new file name
1834              */
1835             const OUString sStdTmplFile = SfxObjectFactory::GetStandardTemplate( sDocServiceName );
1836             if ( INetURLObject( sStdTmplFile ) == INetURLObject( aTemplateToRemoveTargetURL ) )
1837             {
1838                 SfxObjectFactory::SetStandardTemplate( sDocServiceName, aNewTemplateTargetURL );
1839             }
1840         }
1841 
1842         if ( bRemoveOldTemplateContent )
1843             removeContent( aTemplateToRemove );
1844 
1845         // add the template to hierarchy
1846         return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aMediaType );
1847     }
1848     catch( Exception& )
1849     {
1850         // the template was not stored
1851         return false;
1852     }
1853 }
1854 
1855 
addTemplate(const OUString & rGroupName,const OUString & rTemplateName,const OUString & rSourceURL)1856 bool SfxDocTplService_Impl::addTemplate( const OUString& rGroupName,
1857                                              const OUString& rTemplateName,
1858                                              const OUString& rSourceURL )
1859 {
1860     ::osl::MutexGuard aGuard( maMutex );
1861 
1862     // Check, whether or not there is a group with this name
1863     // Return false, if there is no group with the given name
1864     Content         aGroup, aTemplate, aTargetGroup;
1865     INetURLObject   aGroupObj( maRootURL );
1866 
1867     aGroupObj.insertName( rGroupName, false,
1868                       INetURLObject::LAST_SEGMENT,
1869                       INetURLObject::EncodeMechanism::All );
1870     const OUString aGroupURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1871 
1872     if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
1873         return false;
1874 
1875     // Check, if there's a template with the given name in this group
1876     // Return false, if there already is a template
1877     aGroupObj.insertName( rTemplateName, false,
1878                       INetURLObject::LAST_SEGMENT,
1879                       INetURLObject::EncodeMechanism::All );
1880     const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
1881 
1882     if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
1883         return false;
1884 
1885     // get the target url of the group
1886     OUString    aTargetURL;
1887     Any         aValue;
1888 
1889     if ( getProperty( aGroup, TARGET_DIR_URL, aValue ) )
1890         aValue >>= aTargetURL;
1891 
1892     if ( aTargetURL.isEmpty() )
1893     {
1894         aTargetURL = CreateNewGroupFsys( rGroupName, aGroup );
1895 
1896         if ( aTargetURL.isEmpty() )
1897             return false;
1898     }
1899 
1900     // Get the content type
1901     OUString aTitle, aType;
1902 
1903     bool bDocHasTitle = false;
1904     getTitleFromURL( rSourceURL, aTitle, aType, bDocHasTitle );
1905 
1906     INetURLObject   aSourceObj( rSourceURL );
1907     if ( rTemplateName == aTitle )
1908     {
1909         // addTemplate will sometimes be called just to add an entry in the
1910         // hierarchy; the target URL and the source URL will be the same in
1911         // this scenario
1912         // TODO/LATER: get rid of this old hack
1913 
1914         INetURLObject   aTargetObj( aTargetURL );
1915 
1916         aTargetObj.insertName( rTemplateName, false,
1917                       INetURLObject::LAST_SEGMENT,
1918                       INetURLObject::EncodeMechanism::All );
1919         aTargetObj.setExtension( aSourceObj.getExtension() );
1920 
1921         const OUString aTargetURL2 = aTargetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
1922 
1923         if ( aTargetURL2 == rSourceURL )
1924             return addEntry( aGroup, rTemplateName, aTargetURL2, aType );
1925     }
1926 
1927     // copy the template into the new group (targeturl)
1928 
1929     INetURLObject aTmpURL( aSourceObj );
1930     aTmpURL.CutExtension();
1931     const OUString aPattern {aTmpURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )};
1932 
1933     const OUString aNewTemplateTargetURL {CreateNewUniqueFileWithPrefix( aTargetURL, aPattern, aSourceObj.getExtension() )};
1934     INetURLObject aNewTemplateTargetObj( aNewTemplateTargetURL );
1935     const OUString aNewTemplateTargetName {aNewTemplateTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset )};
1936     if ( aNewTemplateTargetURL.isEmpty() || aNewTemplateTargetName.isEmpty() )
1937         return false;
1938 
1939     // get access to source file
1940     Content aSourceContent;
1941     uno::Reference < ucb::XCommandEnvironment > xEnv;
1942     INetURLObject   aSourceURL( rSourceURL );
1943     if( ! Content::create( aSourceURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
1944         return false;
1945 
1946     if( ! Content::create( aTargetURL, xEnv, comphelper::getProcessComponentContext(), aTargetGroup ) )
1947         return false;
1948 
1949     // transfer source file
1950     try
1951     {
1952         aTargetGroup.transferContent( aSourceContent,
1953                                       InsertOperation::Copy,
1954                                       aNewTemplateTargetName,
1955                                       NameClash::OVERWRITE,
1956                                       aType );
1957 
1958         // allow to edit the added template
1959         Content aResultContent;
1960         if ( Content::create( aNewTemplateTargetURL, xEnv, comphelper::getProcessComponentContext(), aResultContent ) )
1961         {
1962             const OUString aPropertyName( "IsReadOnly" );
1963             uno::Any aProperty;
1964             bool bReadOnly = false;
1965             if ( getProperty( aResultContent, aPropertyName, aProperty ) && ( aProperty >>= bReadOnly ) && bReadOnly )
1966                 setProperty( aResultContent, aPropertyName, uno::makeAny( false ) );
1967         }
1968     }
1969     catch ( ContentCreationException& )
1970     { return false; }
1971     catch ( Exception& )
1972     { return false; }
1973 
1974 
1975     // either the document has title and it is the same as requested, or we have to set it
1976     bool bCorrectTitle = ( bDocHasTitle && aTitle == rTemplateName );
1977     if ( !bCorrectTitle )
1978     {
1979         if ( !bDocHasTitle )
1980         {
1981             INetURLObject aNewTmpObj( aNewTemplateTargetObj );
1982             aNewTmpObj.CutExtension();
1983             bCorrectTitle = ( aNewTmpObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ) == rTemplateName );
1984         }
1985 
1986         if ( !bCorrectTitle )
1987             bCorrectTitle = setTitleForURL( aNewTemplateTargetURL, rTemplateName );
1988     }
1989 
1990     if ( bCorrectTitle )
1991     {
1992         // create a new entry in the hierarchy
1993         return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aType );
1994     }
1995 
1996     // TODO/LATER: The user could be notified here that the renaming has failed
1997     // create a new entry in the hierarchy
1998     addEntry( aGroup, aTitle, aNewTemplateTargetURL, aType );
1999     return false;
2000 }
2001 
isInternalTemplateDir(const OUString & rURL) const2002 bool SfxDocTplService_Impl::isInternalTemplateDir(const OUString& rURL) const
2003 {
2004     return std::any_of(maInternalTemplateDirs.begin(), maInternalTemplateDirs.end(),
2005         [&rURL](const OUString& rDir) { return ::utl::UCBContentHelper::IsSubPath(rDir, rURL); });
2006 }
2007 
findParentTemplateDir(const OUString & rURL) const2008 OUString SfxDocTplService_Impl::findParentTemplateDir(const OUString& rURL) const
2009 {
2010     const OUString* pDirs = std::find_if(maTemplateDirs.begin(), maTemplateDirs.end(),
2011         [&rURL](const OUString& rDir) { return ::utl::UCBContentHelper::IsSubPath(rDir, rURL); });
2012     if (pDirs != maTemplateDirs.end())
2013         return *pDirs;
2014     return OUString();
2015 }
2016 
removeTemplate(const OUString & rGroupName,const OUString & rTemplateName)2017 bool SfxDocTplService_Impl::removeTemplate( const OUString& rGroupName,
2018                                                 const OUString& rTemplateName )
2019 {
2020     ::osl::MutexGuard aGuard( maMutex );
2021 
2022     // Check, whether or not there is a group with this name
2023     // Return false, if there is no group with the given name
2024     Content         aGroup, aTemplate;
2025     INetURLObject   aGroupObj( maRootURL );
2026 
2027     aGroupObj.insertName( rGroupName, false,
2028                       INetURLObject::LAST_SEGMENT,
2029                       INetURLObject::EncodeMechanism::All );
2030     const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
2031 
2032     if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
2033         return false;
2034 
2035     // Check, if there's a template with the given name in this group
2036     // Return false, if there is no template
2037     aGroupObj.insertName( rTemplateName, false,
2038                       INetURLObject::LAST_SEGMENT,
2039                       INetURLObject::EncodeMechanism::All );
2040     const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
2041 
2042     if ( !Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
2043         return false;
2044 
2045     // get the target URL from the template
2046     OUString    aTargetURL;
2047     Any         aValue;
2048 
2049     if ( getProperty( aTemplate, TARGET_URL, aValue ) )
2050         aValue >>= aTargetURL;
2051 
2052     // delete the target template
2053     if ( !aTargetURL.isEmpty() )
2054     {
2055         if (isInternalTemplateDir(aTargetURL))
2056             return false;
2057 
2058         removeContent( aTargetURL );
2059     }
2060 
2061     // delete the template entry
2062     return removeContent( aTemplate );
2063 }
2064 
2065 
renameTemplate(const OUString & rGroupName,const OUString & rOldName,const OUString & rNewName)2066 bool SfxDocTplService_Impl::renameTemplate( const OUString& rGroupName,
2067                                                 const OUString& rOldName,
2068                                                 const OUString& rNewName )
2069 {
2070     ::osl::MutexGuard aGuard( maMutex );
2071 
2072     // Check, whether or not there is a group with this name
2073     // Return false, if there is no group with the given name
2074     Content         aGroup, aTemplate;
2075     INetURLObject   aGroupObj( maRootURL );
2076 
2077     aGroupObj.insertName( rGroupName, false,
2078                       INetURLObject::LAST_SEGMENT,
2079                       INetURLObject::EncodeMechanism::All );
2080     const OUString aGroupURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
2081 
2082     if ( ! Content::create( aGroupURL, maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
2083         return false;
2084 
2085     // Check, if there's a template with the new name in this group
2086     // Return false, if there is one
2087     aGroupObj.insertName( rNewName, false,
2088                       INetURLObject::LAST_SEGMENT,
2089                       INetURLObject::EncodeMechanism::All );
2090     OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
2091 
2092     if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
2093         return false;
2094 
2095     // Check, if there's a template with the old name in this group
2096     // Return false, if there is no template
2097     aGroupObj.removeSegment();
2098     aGroupObj.insertName( rOldName, false,
2099                       INetURLObject::LAST_SEGMENT,
2100                       INetURLObject::EncodeMechanism::All );
2101     aTemplateURL = aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2102 
2103     if ( !Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
2104         return false;
2105 
2106     OUString    aTemplateTargetURL;
2107     Any         aTargetValue;
2108 
2109     if ( getProperty( aTemplate, TARGET_URL, aTargetValue ) )
2110         aTargetValue >>= aTemplateTargetURL;
2111 
2112     if ( !setTitleForURL( aTemplateTargetURL, rNewName ) )
2113         return false;
2114 
2115     // rename the template entry in the cache
2116     Any         aTitleValue;
2117     aTitleValue <<= rNewName;
2118 
2119     return setProperty( aTemplate, TITLE, aTitleValue );
2120 }
2121 
2122 
2123 class SfxDocTplService: public ::cppu::WeakImplHelper< css::lang::XLocalizable, css::frame::XDocumentTemplates, css::lang::XServiceInfo >
2124 {
2125     std::unique_ptr<SfxDocTplService_Impl>      pImp;
2126 
2127 public:
2128     explicit SfxDocTplService( const css::uno::Reference < uno::XComponentContext >& xContext );
2129 
getImplementationName()2130     virtual OUString SAL_CALL getImplementationName() override
2131     {
2132         return "com.sun.star.comp.sfx2.DocumentTemplates";
2133     }
2134 
supportsService(OUString const & ServiceName)2135     virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
2136     {
2137         return cppu::supportsService(this, ServiceName);
2138     }
2139 
getSupportedServiceNames()2140     virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
2141     {
2142         css::uno::Sequence< OUString > aSeq { "com.sun.star.frame.DocumentTemplates" };
2143         return aSeq;
2144     }
2145 
2146 
2147     // --- XLocalizable ---
2148     void SAL_CALL                   setLocale( const css::lang::Locale & eLocale ) override;
2149     css::lang::Locale SAL_CALL              getLocale() override;
2150 
2151     // --- XDocumentTemplates ---
2152     css::uno::Reference< css::ucb::XContent > SAL_CALL  getContent() override;
2153     sal_Bool SAL_CALL               storeTemplate( const OUString& GroupName,
2154                                                    const OUString& TemplateName,
2155                                                    const css::uno::Reference< css::frame::XStorable >& Storable ) override;
2156     sal_Bool SAL_CALL               addTemplate( const OUString& GroupName,
2157                                                  const OUString& TemplateName,
2158                                                  const OUString& SourceURL ) override;
2159     sal_Bool SAL_CALL               removeTemplate( const OUString& GroupName,
2160                                                     const OUString& TemplateName ) override;
2161     sal_Bool SAL_CALL               renameTemplate( const OUString& GroupName,
2162                                                     const OUString& OldTemplateName,
2163                                                     const OUString& NewTemplateName ) override;
2164     sal_Bool SAL_CALL               addGroup( const OUString& GroupName ) override;
2165     sal_Bool SAL_CALL               removeGroup( const OUString& GroupName ) override;
2166     sal_Bool SAL_CALL               renameGroup( const OUString& OldGroupName,
2167                                                  const OUString& NewGroupName ) override;
2168     void SAL_CALL                   update() override;
2169 };
2170 
2171 
SfxDocTplService(const uno::Reference<XComponentContext> & xContext)2172 SfxDocTplService::SfxDocTplService( const uno::Reference< XComponentContext >& xContext )
2173 {
2174     pImp.reset( new SfxDocTplService_Impl(xContext) );
2175 }
2176 
2177 
2178 
2179 //--- XLocalizable ---
2180 
2181 
getLocale()2182 lang::Locale SAL_CALL SfxDocTplService::getLocale()
2183 {
2184     return pImp->getLocale();
2185 }
2186 
2187 
setLocale(const lang::Locale & rLocale)2188 void SAL_CALL SfxDocTplService::setLocale( const lang::Locale & rLocale )
2189 {
2190     pImp->setLocale( rLocale );
2191 }
2192 
2193 
2194 //--- XDocumentTemplates ---
2195 
getContent()2196 uno::Reference< ucb::XContent > SAL_CALL SfxDocTplService::getContent()
2197 {
2198     if ( pImp->init() )
2199         return pImp->getContent().get();
2200     return nullptr;
2201 }
2202 
2203 
storeTemplate(const OUString & GroupName,const OUString & TemplateName,const uno::Reference<frame::XStorable> & Storable)2204 sal_Bool SAL_CALL SfxDocTplService::storeTemplate( const OUString& GroupName,
2205                                                    const OUString& TemplateName,
2206                                                    const uno::Reference< frame::XStorable >& Storable )
2207 {
2208     return pImp->init() && pImp->storeTemplate( GroupName, TemplateName, Storable );
2209 }
2210 
2211 
addTemplate(const OUString & rGroupName,const OUString & rTemplateName,const OUString & rSourceURL)2212 sal_Bool SAL_CALL SfxDocTplService::addTemplate( const OUString& rGroupName,
2213                                                  const OUString& rTemplateName,
2214                                                  const OUString& rSourceURL )
2215 {
2216     return pImp->init() && pImp->addTemplate( rGroupName, rTemplateName, rSourceURL );
2217 }
2218 
2219 
removeTemplate(const OUString & rGroupName,const OUString & rTemplateName)2220 sal_Bool SAL_CALL SfxDocTplService::removeTemplate( const OUString& rGroupName,
2221                                                     const OUString& rTemplateName )
2222 {
2223     return pImp->init() && pImp->removeTemplate( rGroupName, rTemplateName );
2224 }
2225 
2226 
renameTemplate(const OUString & rGroupName,const OUString & rOldName,const OUString & rNewName)2227 sal_Bool SAL_CALL SfxDocTplService::renameTemplate( const OUString& rGroupName,
2228                                                     const OUString& rOldName,
2229                                                     const OUString& rNewName )
2230 {
2231     if ( rOldName == rNewName )
2232         return true;
2233 
2234     return pImp->init() && pImp->renameTemplate( rGroupName, rOldName, rNewName );
2235 }
2236 
2237 
addGroup(const OUString & rGroupName)2238 sal_Bool SAL_CALL SfxDocTplService::addGroup( const OUString& rGroupName )
2239 {
2240     return pImp->init() && pImp->addGroup( rGroupName );
2241 }
2242 
2243 
removeGroup(const OUString & rGroupName)2244 sal_Bool SAL_CALL SfxDocTplService::removeGroup( const OUString& rGroupName )
2245 {
2246     return pImp->init() && pImp->removeGroup( rGroupName );
2247 }
2248 
2249 
renameGroup(const OUString & rOldName,const OUString & rNewName)2250 sal_Bool SAL_CALL SfxDocTplService::renameGroup( const OUString& rOldName,
2251                                                  const OUString& rNewName )
2252 {
2253     if ( rOldName == rNewName )
2254         return true;
2255 
2256     return pImp->init() && pImp->renameGroup( rOldName, rNewName );
2257 }
2258 
2259 
update()2260 void SAL_CALL SfxDocTplService::update()
2261 {
2262     if ( pImp->init() )
2263         pImp->update();
2264 }
2265 
WaitWindow_Impl()2266 WaitWindow_Impl::WaitWindow_Impl() : WorkWindow(nullptr, WB_BORDER | WB_3DLOOK)
2267 {
2268     tools::Rectangle aRect(0, 0, 300, 30000);
2269     maText = SfxResId(RID_CNT_STR_WAITING);
2270     maRect = GetTextRect(aRect, maText, gnTextStyle);
2271     aRect = maRect;
2272     aRect.AdjustRight(2 * X_OFFSET );
2273     aRect.AdjustBottom(2 * Y_OFFSET );
2274     maRect.SetPos(Point(X_OFFSET, Y_OFFSET));
2275     SetOutputSizePixel(aRect.GetSize());
2276 
2277     Show();
2278     Update();
2279     Flush();
2280 }
2281 
2282 
~WaitWindow_Impl()2283 WaitWindow_Impl::~WaitWindow_Impl()
2284 {
2285     disposeOnce();
2286 }
2287 
dispose()2288 void  WaitWindow_Impl::dispose()
2289 {
2290     Hide();
2291     WorkWindow::dispose();
2292 }
2293 
2294 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)2295 void WaitWindow_Impl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
2296 {
2297     rRenderContext.DrawText(maRect, maText, gnTextStyle);
2298 }
2299 
addHierGroup(GroupList_Impl & rList,const OUString & rTitle,const OUString & rOwnURL)2300 void SfxDocTplService_Impl::addHierGroup( GroupList_Impl& rList,
2301                                           const OUString& rTitle,
2302                                           const OUString& rOwnURL )
2303 {
2304     // now get the content of the Group
2305     Content aContent;
2306     uno::Reference<XResultSet> xResultSet;
2307     Sequence<OUString> aProps(3);
2308 
2309     aProps[0] = TITLE;
2310     aProps[1] = TARGET_URL;
2311     aProps[2] = PROPERTY_TYPE;
2312 
2313     try
2314     {
2315         aContent = Content(rOwnURL, maCmdEnv, comphelper::getProcessComponentContext());
2316         xResultSet = aContent.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY );
2317     }
2318     catch (ContentCreationException&)
2319     {
2320         SAL_WARN( "sfx.doc", "addHierGroup: ContentCreationException" );
2321     }
2322     catch (Exception&) {}
2323 
2324     if ( !xResultSet.is() )
2325         return;
2326 
2327     GroupData_Impl *pGroup = new GroupData_Impl( rTitle );
2328     pGroup->setHierarchy( true );
2329     pGroup->setHierarchyURL( rOwnURL );
2330     rList.push_back( std::unique_ptr<GroupData_Impl>(pGroup) );
2331 
2332     uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
2333     uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
2334 
2335     try
2336     {
2337         while ( xResultSet->next() )
2338         {
2339             bool             bUpdateType = false;
2340             DocTemplates_EntryData_Impl  *pData;
2341 
2342             const OUString aTitle( xRow->getString( 1 ) );
2343             const OUString aTargetDir( xRow->getString( 2 ) );
2344             OUString aType( xRow->getString( 3 ) );
2345             const OUString aHierURL {xContentAccess->queryContentIdentifierString()};
2346 
2347             if ( aType.isEmpty() )
2348             {
2349                 OUString aTmpTitle;
2350 
2351                 bool bDocHasTitle = false;
2352                 getTitleFromURL( aTargetDir, aTmpTitle, aType, bDocHasTitle );
2353 
2354                 if ( !aType.isEmpty() )
2355                     bUpdateType = true;
2356             }
2357 
2358             pData = pGroup->addEntry( aTitle, aTargetDir, aType, aHierURL );
2359             pData->setUpdateType( bUpdateType );
2360         }
2361     }
2362     catch ( Exception& ) {}
2363 }
2364 
2365 
addFsysGroup(GroupList_Impl & rList,const OUString & rTitle,const OUString & rUITitle,const OUString & rOwnURL,bool bWriteableGroup)2366 void SfxDocTplService_Impl::addFsysGroup( GroupList_Impl& rList,
2367                                           const OUString& rTitle,
2368                                           const OUString& rUITitle,
2369                                           const OUString& rOwnURL,
2370                                           bool bWriteableGroup )
2371 {
2372     OUString aTitle;
2373 
2374     if ( rUITitle.isEmpty() )
2375     {
2376         // reserved FS names that should not be used
2377         if ( rTitle == "wizard" )
2378             return;
2379         else if ( rTitle == "internal" )
2380             return;
2381 
2382         aTitle = getLongName( rTitle );
2383     }
2384     else
2385         aTitle = rUITitle;
2386 
2387     if ( aTitle.isEmpty() )
2388         return;
2389 
2390     GroupData_Impl* pGroup = nullptr;
2391     for (const std::unique_ptr<GroupData_Impl>& i : rList)
2392     {
2393         if ( i->getTitle() == aTitle )
2394         {
2395             pGroup = i.get();
2396             break;
2397         }
2398     }
2399 
2400     if ( !pGroup )
2401     {
2402         pGroup = new GroupData_Impl( aTitle );
2403         rList.push_back( std::unique_ptr<GroupData_Impl>(pGroup) );
2404     }
2405 
2406     if ( bWriteableGroup )
2407         pGroup->setTargetURL( rOwnURL );
2408 
2409     pGroup->setInUse();
2410 
2411     // now get the content of the Group
2412     Content                 aContent;
2413     uno::Reference< XResultSet > xResultSet;
2414     Sequence< OUString >    aProps { TITLE };
2415 
2416     try
2417     {
2418         // this method is only used during checking of the available template-folders
2419         // that should happen quietly
2420         uno::Reference< XCommandEnvironment > aQuietEnv;
2421         aContent = Content( rOwnURL, aQuietEnv, comphelper::getProcessComponentContext() );
2422         xResultSet = aContent.createCursor( aProps, INCLUDE_DOCUMENTS_ONLY );
2423     }
2424     catch ( Exception& ) {}
2425 
2426     if ( !xResultSet.is() )
2427         return;
2428 
2429     uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
2430     uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
2431 
2432     try
2433     {
2434         while ( xResultSet->next() )
2435         {
2436             OUString aChildTitle( xRow->getString( 1 ) );
2437             const OUString aTargetURL {xContentAccess->queryContentIdentifierString()};
2438             OUString aType;
2439 
2440             if ( aChildTitle == "sfx.tlx" || aChildTitle == "groupuinames.xml" )
2441                 continue;
2442 
2443             bool bDocHasTitle = false;
2444             getTitleFromURL( aTargetURL, aChildTitle, aType, bDocHasTitle );
2445 
2446             pGroup->addEntry( aChildTitle, aTargetURL, aType, OUString() );
2447         }
2448     }
2449     catch ( Exception& ) {}
2450 }
2451 
2452 
createFromContent(GroupList_Impl & rList,Content & rContent,bool bHierarchy,bool bWriteableContent)2453 void SfxDocTplService_Impl::createFromContent( GroupList_Impl& rList,
2454                                                Content &rContent,
2455                                                bool bHierarchy,
2456                                                bool bWriteableContent )
2457 {
2458     const OUString aTargetURL {rContent.get()->getIdentifier()->getContentIdentifier()};
2459 
2460     // when scanning the file system, we have to add the 'standard' group, too
2461     if ( ! bHierarchy )
2462     {
2463         const OUString aUIStdTitle {getLongName( STANDARD_FOLDER )};
2464         addFsysGroup( rList, OUString(), aUIStdTitle, aTargetURL, bWriteableContent );
2465     }
2466 
2467     // search for predefined UI names
2468     INetURLObject aLayerObj( aTargetURL );
2469 
2470     // TODO/LATER: Use hashmap in future
2471     std::vector< beans::StringPair > aUINames;
2472     if ( !bHierarchy )
2473         aUINames = ReadUINamesForTemplateDir_Impl( aLayerObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2474 
2475     uno::Reference< XResultSet > xResultSet;
2476     Sequence< OUString > aProps { TITLE };
2477 
2478     try
2479     {
2480         xResultSet = rContent.createCursor( aProps, INCLUDE_FOLDERS_ONLY );
2481     }
2482     catch ( Exception& ) {}
2483 
2484     if ( !xResultSet.is() )
2485         return;
2486 
2487     uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
2488     uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );
2489 
2490     try
2491     {
2492         while ( xResultSet->next() )
2493         {
2494             // TODO/LATER: clarify the encoding of the Title
2495             const OUString aTitle( xRow->getString( 1 ) );
2496             const OUString aTargetSubfolderURL( xContentAccess->queryContentIdentifierString() );
2497 
2498             if ( bHierarchy )
2499                 addHierGroup( rList, aTitle, aTargetSubfolderURL );
2500             else
2501             {
2502                 OUString aUITitle;
2503                 for (const beans::StringPair & rUIName : aUINames)
2504                     if ( rUIName.First == aTitle )
2505                     {
2506                         aUITitle = rUIName.Second;
2507                         break;
2508                     }
2509 
2510                 addFsysGroup( rList, aTitle, aUITitle, aTargetSubfolderURL, bWriteableContent );
2511             }
2512         }
2513     }
2514     catch ( Exception& ) {}
2515 }
2516 
2517 
removeFromHierarchy(DocTemplates_EntryData_Impl const * pData)2518 void SfxDocTplService_Impl::removeFromHierarchy( DocTemplates_EntryData_Impl const *pData )
2519 {
2520     Content aTemplate;
2521 
2522     if ( Content::create( pData->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
2523     {
2524         removeContent( aTemplate );
2525     }
2526 }
2527 
2528 
addToHierarchy(GroupData_Impl const * pGroup,DocTemplates_EntryData_Impl const * pData)2529 void SfxDocTplService_Impl::addToHierarchy( GroupData_Impl const *pGroup,
2530                                             DocTemplates_EntryData_Impl const *pData )
2531 {
2532     Content aGroup, aTemplate;
2533 
2534     if ( ! Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
2535         return;
2536 
2537     // Check, if there's a template with the given name in this group
2538     // Return if there is already a template
2539     INetURLObject aGroupObj( pGroup->getHierarchyURL() );
2540 
2541     aGroupObj.insertName( pData->getTitle(), false,
2542                       INetURLObject::LAST_SEGMENT,
2543                       INetURLObject::EncodeMechanism::All );
2544 
2545     const OUString aTemplateURL {aGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
2546 
2547     if ( Content::create( aTemplateURL, maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
2548         return;
2549 
2550     addEntry( aGroup, pData->getTitle(),
2551               pData->getTargetURL(),
2552               pData->getType() );
2553 }
2554 
2555 
updateData(DocTemplates_EntryData_Impl const * pData)2556 void SfxDocTplService_Impl::updateData( DocTemplates_EntryData_Impl const *pData )
2557 {
2558     Content aTemplate;
2559 
2560     if ( ! Content::create( pData->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aTemplate ) )
2561         return;
2562 
2563     if ( pData->getUpdateType() )
2564     {
2565         setProperty( aTemplate, PROPERTY_TYPE, makeAny( pData->getType() ) );
2566     }
2567 
2568     if ( pData->getUpdateLink() )
2569     {
2570         setProperty( aTemplate, TARGET_URL, makeAny( pData->getTargetURL() ) );
2571     }
2572 }
2573 
2574 
addGroupToHierarchy(GroupData_Impl * pGroup)2575 void SfxDocTplService_Impl::addGroupToHierarchy( GroupData_Impl *pGroup )
2576 {
2577     Content aGroup;
2578 
2579     INetURLObject aNewGroupObj( maRootURL );
2580     aNewGroupObj.insertName( pGroup->getTitle(), false,
2581           INetURLObject::LAST_SEGMENT,
2582           INetURLObject::EncodeMechanism::All );
2583 
2584     const OUString aNewGroupURL {aNewGroupObj.GetMainURL( INetURLObject::DecodeMechanism::NONE )};
2585 
2586     if ( createFolder( aNewGroupURL, false, false, aGroup ) )
2587     {
2588         setProperty( aGroup, TARGET_DIR_URL, makeAny( pGroup->getTargetURL() ) );
2589         pGroup->setHierarchyURL( aNewGroupURL );
2590 
2591         sal_uIntPtr nCount = pGroup->count();
2592         for ( sal_uIntPtr i=0; i<nCount; i++ )
2593         {
2594             DocTemplates_EntryData_Impl *pData = pGroup->getEntry( i );
2595             addToHierarchy( pGroup, pData ); // add entry to hierarchy
2596         }
2597     }
2598 }
2599 
2600 
removeFromHierarchy(GroupData_Impl const * pGroup)2601 void SfxDocTplService_Impl::removeFromHierarchy( GroupData_Impl const *pGroup )
2602 {
2603     Content aGroup;
2604 
2605     if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, comphelper::getProcessComponentContext(), aGroup ) )
2606     {
2607         removeContent( aGroup );
2608     }
2609 }
2610 
2611 
GroupData_Impl(const OUString & rTitle)2612 GroupData_Impl::GroupData_Impl( const OUString& rTitle )
2613 {
2614     maTitle = rTitle;
2615     mbInUse = false;
2616     mbInHierarchy = false;
2617 }
2618 
2619 
addEntry(const OUString & rTitle,const OUString & rTargetURL,const OUString & rType,const OUString & rHierURL)2620 DocTemplates_EntryData_Impl* GroupData_Impl::addEntry( const OUString& rTitle,
2621                                           const OUString& rTargetURL,
2622                                           const OUString& rType,
2623                                           const OUString& rHierURL )
2624 {
2625     DocTemplates_EntryData_Impl* pData = nullptr;
2626     bool EntryFound = false;
2627 
2628     for (auto const & p : maEntries)
2629     {
2630         pData = p.get();
2631         if ( pData->getTitle() == rTitle )
2632         {
2633             EntryFound = true;
2634             break;
2635         }
2636     }
2637 
2638     if ( !EntryFound )
2639     {
2640         pData = new DocTemplates_EntryData_Impl( rTitle );
2641         pData->setTargetURL( rTargetURL );
2642         pData->setType( rType );
2643         if ( !rHierURL.isEmpty() )
2644         {
2645             pData->setHierarchyURL( rHierURL );
2646             pData->setHierarchy( true );
2647         }
2648         maEntries.emplace_back( pData );
2649     }
2650     else
2651     {
2652         if ( !rHierURL.isEmpty() )
2653         {
2654             pData->setHierarchyURL( rHierURL );
2655             pData->setHierarchy( true );
2656         }
2657 
2658         if ( pData->getInHierarchy() )
2659             pData->setInUse();
2660 
2661         if ( rTargetURL != pData->getTargetURL() )
2662         {
2663             pData->setTargetURL( rTargetURL );
2664             pData->setUpdateLink( true );
2665         }
2666     }
2667 
2668     return pData;
2669 }
2670 
2671 
DocTemplates_EntryData_Impl(const OUString & rTitle)2672 DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( const OUString& rTitle )
2673 {
2674     maTitle         = rTitle;
2675     mbInUse         = false;
2676     mbInHierarchy   = false;
2677     mbUpdateType    = false;
2678     mbUpdateLink    = false;
2679 }
2680 
2681 }
2682 
2683 // static
propertyCanContainOfficeDir(const OUString & rPropName)2684 bool SfxURLRelocator_Impl::propertyCanContainOfficeDir(
2685                                         const OUString & rPropName )
2686 {
2687     // Note: TargetURL is handled by UCB itself (because it is a property
2688     //       with a predefined semantic). Additional Core properties introduced
2689     //       be a client app must be handled by the client app itself, because
2690     //       the UCB does not know the semantics of those properties.
2691     return ( rPropName == TARGET_DIR_URL || rPropName == PROPERTY_DIRLIST );
2692 }
2693 
2694 
SfxURLRelocator_Impl(const uno::Reference<XComponentContext> & xContext)2695 SfxURLRelocator_Impl::SfxURLRelocator_Impl( const uno::Reference< XComponentContext > & xContext )
2696 : mxContext( xContext )
2697 {
2698 }
2699 
2700 
~SfxURLRelocator_Impl()2701 SfxURLRelocator_Impl::~SfxURLRelocator_Impl()
2702 {
2703 }
2704 
2705 
initOfficeInstDirs()2706 void SfxURLRelocator_Impl::initOfficeInstDirs()
2707 {
2708     if ( !mxOfficeInstDirs.is() )
2709     {
2710         osl::MutexGuard aGuard( maMutex );
2711         if ( !mxOfficeInstDirs.is() )
2712         {
2713             OSL_ENSURE( mxContext.is(), "No service manager!" );
2714 
2715             mxOfficeInstDirs = theOfficeInstallationDirectories::get(mxContext);
2716         }
2717     }
2718 }
2719 
2720 
implExpandURL(OUString & io_url)2721 void SfxURLRelocator_Impl::implExpandURL( OUString& io_url )
2722 {
2723     const INetURLObject aParser( io_url );
2724     if ( aParser.GetProtocol() != INetProtocol::VndSunStarExpand )
2725         return;
2726 
2727     io_url = aParser.GetURLPath( INetURLObject::DecodeMechanism::WithCharset );
2728     try
2729     {
2730         if ( !mxMacroExpander.is() )
2731         {
2732             mxMacroExpander.set( theMacroExpander::get(mxContext), UNO_SET_THROW );
2733         }
2734         io_url = mxMacroExpander->expandMacros( io_url );
2735     }
2736     catch( const Exception& )
2737     {
2738         DBG_UNHANDLED_EXCEPTION("sfx.doc");
2739     }
2740 }
2741 
2742 
makeRelocatableURL(OUString & rURL)2743 void SfxURLRelocator_Impl::makeRelocatableURL( OUString & rURL )
2744 {
2745     if ( !rURL.isEmpty() )
2746     {
2747         initOfficeInstDirs();
2748         implExpandURL( rURL );
2749         rURL = mxOfficeInstDirs->makeRelocatableURL( rURL );
2750     }
2751 }
2752 
2753 
makeAbsoluteURL(OUString & rURL)2754 void SfxURLRelocator_Impl::makeAbsoluteURL( OUString & rURL )
2755 {
2756     if ( !rURL.isEmpty() )
2757     {
2758         initOfficeInstDirs();
2759         implExpandURL( rURL );
2760         rURL = mxOfficeInstDirs->makeAbsoluteURL( rURL );
2761     }
2762 }
2763 
2764 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_sfx2_DocumentTemplates_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)2765 com_sun_star_comp_sfx2_DocumentTemplates_get_implementation(
2766     css::uno::XComponentContext *context,
2767     css::uno::Sequence<css::uno::Any> const &)
2768 {
2769     return cppu::acquire(new SfxDocTplService(context));
2770 }
2771 
GetStandardGroupString()2772 OUString DocTemplLocaleHelper::GetStandardGroupString()
2773 {
2774     return SfxResId(TEMPLATE_LONG_NAMES_ARY[0]);
2775 }
2776 
2777 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2778