Coverage Report

Created: 2026-08-14 10:22

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