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