Line data Source code
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 :
21 : #include "TemplateScanner.hxx"
22 :
23 : #include <comphelper/processfactory.hxx>
24 : #include <comphelper/componentcontext.hxx>
25 : #include <comphelper/documentconstants.hxx>
26 : #include <comphelper/string.hxx>
27 :
28 : #include <tools/debug.hxx>
29 : #include <osl/mutex.hxx>
30 : #include <vcl/svapp.hxx>
31 : #include <sfx2/doctempl.hxx>
32 : #include <sfx2/templatelocnames.hrc>
33 : #include <com/sun/star/frame/DocumentTemplates.hpp>
34 : #include <com/sun/star/frame/XDocumentTemplates.hpp>
35 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
36 : #include <com/sun/star/ucb/XCommandEnvironment.hpp>
37 : #include <com/sun/star/ucb/XContentAccess.hpp>
38 : #include <com/sun/star/sdbc/XResultSet.hpp>
39 : #include <com/sun/star/sdbc/XRow.hpp>
40 :
41 : #include <set>
42 :
43 : using namespace ::com::sun::star;
44 : using namespace ::com::sun::star::uno;
45 :
46 : namespace {
47 :
48 : const char TITLE[] = "Title";
49 : const char TARGET_DIR_URL[] = "TargetDirURL";
50 : const char DESCRIPTION[] = "TypeDescription";
51 : const char TARGET_URL[] = "TargetURL";
52 :
53 : const char DOCTEMPLATES[] = "com.sun.star.frame.DocumentTemplates";
54 :
55 : // These strings are used to find impress templates in the tree of
56 : // template files. Should probably be determined dynamically.
57 : const char IMPRESS_BIN_TEMPLATE[] = "application/vnd.stardivision.impress";
58 : const char IMPRESS_XML_TEMPLATE[] = MIMETYPE_VND_SUN_XML_IMPRESS_ASCII;
59 : // The following id comes from the bugdoc in #i2764#.
60 : const char IMPRESS_XML_TEMPLATE_B[] = "Impress 2.0";
61 : const char IMPRESS_XML_TEMPLATE_OASIS[] = MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII;
62 :
63 :
64 0 : class FolderDescriptor
65 : {
66 : public:
67 0 : FolderDescriptor (
68 : int nPriority,
69 : const ::rtl::OUString& rsTitle,
70 : const ::rtl::OUString& rsTargetDir,
71 : const ::rtl::OUString& rsContentIdentifier,
72 : const Reference<com::sun::star::ucb::XCommandEnvironment>& rxFolderEnvironment)
73 : : mnPriority(nPriority),
74 : msTitle(rsTitle),
75 : msTargetDir(rsTargetDir),
76 : msContentIdentifier(rsContentIdentifier),
77 0 : mxFolderEnvironment(rxFolderEnvironment)
78 0 : { }
79 : int mnPriority;
80 : ::rtl::OUString msTitle;
81 : ::rtl::OUString msTargetDir;
82 : ::rtl::OUString msContentIdentifier;
83 : // Reference<sdbc::XResultSet> mxFolderResultSet;
84 : Reference<com::sun::star::ucb::XCommandEnvironment> mxFolderEnvironment;
85 :
86 : class Comparator
87 : {
88 : public:
89 0 : bool operator() (const FolderDescriptor& r1, const FolderDescriptor& r2) const
90 0 : { return r1.mnPriority < r2.mnPriority; }
91 : };
92 : };
93 :
94 : /** Use a heuristic based on the URL of a top-level template folder to
95 : assign a priority that is used to sort the folders.
96 : */
97 0 : int Classify (const ::rtl::OUString&, const ::rtl::OUString& rsURL)
98 : {
99 0 : int nPriority (0);
100 :
101 0 : if (rsURL.isEmpty())
102 0 : nPriority = 100;
103 0 : else if (rsURL.indexOf("presnt")>=0)
104 : {
105 0 : nPriority = 30;
106 : }
107 0 : else if (rsURL.indexOf("layout")>=0)
108 : {
109 0 : nPriority = 20;
110 : }
111 0 : else if (rsURL.indexOf("educate")>=0)
112 : {
113 0 : nPriority = 40;
114 : }
115 0 : else if (rsURL.indexOf("finance")>=0)
116 : {
117 0 : nPriority = 40;
118 : }
119 : else
120 : {
121 : // All other folders are taken for user supplied and have the
122 : // highest priority.
123 0 : nPriority = 10;
124 : }
125 :
126 0 : return nPriority;
127 : }
128 :
129 : } // end of anonymous namespace
130 :
131 :
132 :
133 :
134 : namespace sd
135 : {
136 :
137 0 : TemplateEntryCompare::TemplateEntryCompare():
138 : mpStringSorter(new comphelper::string::NaturalStringSorter(
139 : ::comphelper::getProcessComponentContext(),
140 0 : Application::GetSettings().GetLanguageTag().getLocale())) {}
141 :
142 0 : bool TemplateEntryCompare::operator()(TemplateEntry* pA, TemplateEntry* pB) const
143 : {
144 0 : return 0 > mpStringSorter->compare(pA->msTitle, pB->msTitle);
145 : }
146 :
147 0 : void TemplateDir::EnableSorting(bool bSortingEnabled)
148 : {
149 0 : mbSortingEnabled = bSortingEnabled;
150 0 : if (mbSortingEnabled)
151 : {
152 0 : if (mpEntryCompare.get() == NULL)
153 0 : mpEntryCompare.reset(new TemplateEntryCompare);
154 :
155 0 : ::std::sort(maEntries.begin(), maEntries.end(), *mpEntryCompare);
156 : }
157 0 : }
158 :
159 0 : void TemplateDir::InsertEntry(TemplateEntry* pNewEntry)
160 : {
161 0 : if (mbSortingEnabled)
162 : {
163 : ::std::vector<TemplateEntry*>::iterator aPlaceToInsert =
164 0 : ::std::upper_bound(maEntries.begin(), maEntries.end(), pNewEntry, *mpEntryCompare);
165 0 : maEntries.insert(aPlaceToInsert, pNewEntry);
166 : }
167 : else
168 0 : maEntries.push_back(pNewEntry);
169 0 : }
170 :
171 0 : class TemplateScanner::FolderDescriptorList
172 : : public ::std::multiset<FolderDescriptor,FolderDescriptor::Comparator>
173 : {
174 : };
175 :
176 0 : TemplateScanner::TemplateScanner (void)
177 : : meState(INITIALIZE_SCANNING),
178 : maFolderContent(),
179 : mpTemplateDirectory(NULL),
180 : maFolderList(),
181 : mbEntrySortingEnabled(false),
182 : mpLastAddedEntry(NULL),
183 0 : mpFolderDescriptors(new FolderDescriptorList()),
184 : mxTemplateRoot(),
185 : mxFolderEnvironment(),
186 : mxEntryEnvironment(),
187 : mxFolderResultSet(),
188 0 : mxEntryResultSet()
189 : {
190 : // empty;
191 0 : }
192 :
193 :
194 :
195 :
196 0 : TemplateScanner::~TemplateScanner (void)
197 : {
198 0 : mpFolderDescriptors.reset();
199 :
200 : // Delete all entries of the template list that have not been
201 : // transferred to another object.
202 0 : std::vector<TemplateDir*>::iterator I;
203 0 : for (I=maFolderList.begin(); I!=maFolderList.end(); ++I)
204 0 : if (*I != NULL)
205 0 : delete *I;
206 0 : }
207 :
208 :
209 :
210 :
211 0 : TemplateScanner::State TemplateScanner::GetTemplateRoot (void)
212 : {
213 0 : State eNextState (INITIALIZE_FOLDER_SCANNING);
214 :
215 0 : Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
216 0 : Reference<frame::XDocumentTemplates> xTemplates = frame::DocumentTemplates::create(xContext);
217 0 : mxTemplateRoot = xTemplates->getContent();
218 :
219 0 : return eNextState;
220 : }
221 :
222 :
223 :
224 :
225 0 : TemplateScanner::State TemplateScanner::InitializeEntryScanning (void)
226 : {
227 0 : State eNextState (SCAN_ENTRY);
228 :
229 0 : if (maFolderContent.isFolder())
230 : {
231 0 : mxEntryEnvironment = Reference<com::sun::star::ucb::XCommandEnvironment>();
232 :
233 : // We are interested only in three properties: the entry's name,
234 : // its URL, and its content type.
235 0 : Sequence<rtl::OUString> aProps (3);
236 0 : aProps[0] = rtl::OUString(TITLE);
237 0 : aProps[1] = rtl::OUString(TARGET_URL);
238 0 : aProps[2] = rtl::OUString(DESCRIPTION);
239 :
240 : // Create a cursor to iterate over the templates in this folders.
241 0 : ::ucbhelper::ResultSetInclude eInclude = ::ucbhelper::INCLUDE_DOCUMENTS_ONLY;
242 : mxEntryResultSet = Reference<com::sun::star::sdbc::XResultSet>(
243 0 : maFolderContent.createCursor(aProps, eInclude));
244 : }
245 : else
246 0 : eNextState = ERROR;
247 :
248 0 : return eNextState;
249 : }
250 :
251 :
252 :
253 :
254 0 : TemplateScanner::State TemplateScanner::ScanEntry (void)
255 : {
256 0 : State eNextState (ERROR);
257 :
258 0 : Reference<com::sun::star::ucb::XContentAccess> xContentAccess (mxEntryResultSet, UNO_QUERY);
259 0 : Reference<com::sun::star::sdbc::XRow> xRow (mxEntryResultSet, UNO_QUERY);
260 :
261 0 : if (xContentAccess.is() && xRow.is() && mxEntryResultSet.is())
262 : {
263 0 : if (mxEntryResultSet->next())
264 : {
265 0 : ::rtl::OUString sTitle (xRow->getString (1));
266 0 : ::rtl::OUString sTargetURL (xRow->getString (2));
267 0 : ::rtl::OUString sContentType (xRow->getString (3));
268 :
269 0 : ::rtl::OUString aId = xContentAccess->queryContentIdentifierString();
270 0 : ::ucbhelper::Content aContent = ::ucbhelper::Content (aId, mxEntryEnvironment, comphelper::getProcessComponentContext());
271 0 : if (aContent.isDocument ())
272 : {
273 : // Check whether the entry is an impress template. If so
274 : // add a new entry to the resulting list (which is created
275 : // first if necessary).
276 0 : if ( (sContentType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE)
277 0 : || (sContentType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(IMPRESS_XML_TEMPLATE_OASIS)))
278 0 : || (sContentType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(IMPRESS_BIN_TEMPLATE)))
279 0 : || (sContentType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(IMPRESS_XML_TEMPLATE)))
280 0 : || (sContentType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(IMPRESS_XML_TEMPLATE_B))))
281 : {
282 : ::rtl::OUString sLocalisedTitle = SfxDocumentTemplates::ConvertResourceString(
283 0 : STR_TEMPLATE_NAME1_DEF, STR_TEMPLATE_NAME1, NUM_TEMPLATE_NAMES, sTitle );
284 0 : mpLastAddedEntry = new TemplateEntry(sLocalisedTitle, sTargetURL);
285 0 : mpTemplateDirectory->InsertEntry(mpLastAddedEntry);
286 : }
287 : }
288 :
289 : // Continue scanning entries.
290 0 : eNextState = SCAN_ENTRY;
291 : }
292 : else
293 : {
294 0 : if (mpTemplateDirectory->maEntries.empty())
295 : {
296 0 : delete mpTemplateDirectory;
297 0 : mpTemplateDirectory = NULL;
298 : }
299 : else
300 : {
301 0 : SolarMutexGuard aGuard;
302 0 : maFolderList.push_back(mpTemplateDirectory);
303 : }
304 :
305 : // Continue with scanning the next folder.
306 0 : eNextState = SCAN_FOLDER;
307 : }
308 : }
309 :
310 0 : return eNextState;
311 : }
312 :
313 :
314 :
315 :
316 0 : TemplateScanner::State TemplateScanner::InitializeFolderScanning (void)
317 : {
318 0 : State eNextState (ERROR);
319 :
320 0 : mxFolderResultSet = Reference<sdbc::XResultSet>();
321 :
322 : try
323 : {
324 : // Create content for template folders.
325 0 : mxFolderEnvironment = Reference<com::sun::star::ucb::XCommandEnvironment>();
326 0 : ::ucbhelper::Content aTemplateDir (mxTemplateRoot, mxFolderEnvironment, comphelper::getProcessComponentContext());
327 :
328 : // Define the list of properties we are interested in.
329 0 : Sequence<rtl::OUString> aProps (2);
330 0 : aProps[0] = rtl::OUString(TITLE);
331 0 : aProps[1] = rtl::OUString(TARGET_DIR_URL);
332 :
333 : // Create an cursor to iterate over the template folders.
334 0 : ::ucbhelper::ResultSetInclude eInclude = ::ucbhelper::INCLUDE_FOLDERS_ONLY;
335 : mxFolderResultSet = Reference<sdbc::XResultSet>(
336 0 : aTemplateDir.createCursor(aProps, eInclude));
337 0 : if (mxFolderResultSet.is())
338 0 : eNextState = GATHER_FOLDER_LIST;
339 : }
340 0 : catch (::com::sun::star::uno::Exception&)
341 : {
342 0 : eNextState = ERROR;
343 : }
344 :
345 0 : return eNextState;
346 : }
347 :
348 :
349 :
350 :
351 0 : TemplateScanner::State TemplateScanner::GatherFolderList (void)
352 : {
353 0 : State eNextState (ERROR);
354 :
355 0 : Reference<com::sun::star::ucb::XContentAccess> xContentAccess (mxFolderResultSet, UNO_QUERY);
356 0 : if (xContentAccess.is() && mxFolderResultSet.is())
357 : {
358 0 : while (mxFolderResultSet->next())
359 : {
360 0 : Reference<sdbc::XRow> xRow (mxFolderResultSet, UNO_QUERY);
361 0 : if (xRow.is())
362 : {
363 0 : ::rtl::OUString sTitle (xRow->getString (1));
364 0 : ::rtl::OUString sTargetDir (xRow->getString (2));
365 0 : ::rtl::OUString aId = xContentAccess->queryContentIdentifierString();
366 :
367 0 : mpFolderDescriptors->insert(
368 : FolderDescriptor(
369 : Classify(sTitle,sTargetDir),
370 : sTitle,
371 : sTargetDir,
372 : aId,
373 0 : mxFolderEnvironment));
374 : }
375 0 : }
376 :
377 0 : eNextState = SCAN_FOLDER;
378 : }
379 :
380 0 : return eNextState;
381 : }
382 :
383 :
384 :
385 :
386 0 : TemplateScanner::State TemplateScanner::ScanFolder (void)
387 : {
388 0 : State eNextState (ERROR);
389 :
390 0 : if (mpFolderDescriptors->size() > 0)
391 : {
392 0 : FolderDescriptor aDescriptor (*mpFolderDescriptors->begin());
393 0 : mpFolderDescriptors->erase(mpFolderDescriptors->begin());
394 :
395 0 : ::rtl::OUString sTitle (aDescriptor.msTitle);
396 0 : ::rtl::OUString sTargetDir (aDescriptor.msTargetDir);
397 0 : ::rtl::OUString aId (aDescriptor.msContentIdentifier);
398 :
399 0 : maFolderContent = ::ucbhelper::Content (aId, aDescriptor.mxFolderEnvironment, comphelper::getProcessComponentContext());
400 0 : if (maFolderContent.isFolder())
401 : {
402 : // Scan the folder and insert it into the list of template
403 : // folders.
404 0 : mpTemplateDirectory = new TemplateDir (sTitle, sTargetDir);
405 0 : if (mpTemplateDirectory != NULL)
406 : {
407 0 : mpTemplateDirectory->EnableSorting(mbEntrySortingEnabled);
408 : // Continue with scanning all entries in the folder.
409 0 : eNextState = INITIALIZE_ENTRY_SCAN;
410 : }
411 0 : }
412 : }
413 : else
414 : {
415 0 : eNextState = DONE;
416 : }
417 :
418 0 : return eNextState;
419 : }
420 :
421 :
422 :
423 :
424 0 : void TemplateScanner::Scan (void)
425 : {
426 0 : while (HasNextStep())
427 0 : RunNextStep();
428 0 : }
429 :
430 :
431 :
432 :
433 0 : std::vector<TemplateDir*>& TemplateScanner::GetFolderList (void)
434 : {
435 0 : return maFolderList;
436 : }
437 :
438 :
439 :
440 :
441 0 : void TemplateScanner::RunNextStep (void)
442 : {
443 0 : switch (meState)
444 : {
445 : case INITIALIZE_SCANNING:
446 0 : meState = GetTemplateRoot();
447 0 : break;
448 :
449 : case INITIALIZE_FOLDER_SCANNING:
450 0 : meState = InitializeFolderScanning();
451 0 : break;
452 :
453 : case SCAN_FOLDER:
454 0 : meState = ScanFolder();
455 0 : break;
456 :
457 : case GATHER_FOLDER_LIST:
458 0 : meState = GatherFolderList();
459 0 : break;
460 :
461 : case INITIALIZE_ENTRY_SCAN:
462 0 : meState = InitializeEntryScanning();
463 0 : break;
464 :
465 : case SCAN_ENTRY:
466 0 : meState = ScanEntry();
467 0 : break;
468 : default:
469 0 : break;
470 : }
471 :
472 0 : switch (meState)
473 : {
474 : case DONE:
475 : case ERROR:
476 0 : mxTemplateRoot.clear();
477 0 : mxTemplateRoot.clear();
478 0 : mxFolderEnvironment.clear();
479 0 : mxEntryEnvironment.clear();
480 0 : mxFolderResultSet.clear();
481 0 : mxEntryResultSet.clear();
482 0 : mpLastAddedEntry = NULL;
483 0 : break;
484 : default:
485 0 : break;
486 : }
487 0 : }
488 :
489 :
490 :
491 :
492 0 : bool TemplateScanner::HasNextStep (void)
493 : {
494 0 : switch (meState)
495 : {
496 : case DONE:
497 : case ERROR:
498 0 : return false;
499 :
500 : default:
501 0 : return true;
502 : }
503 : }
504 :
505 :
506 :
507 :
508 0 : const TemplateEntry* TemplateScanner::GetLastAddedEntry (void) const
509 : {
510 0 : return mpLastAddedEntry;
511 : }
512 :
513 : }
514 :
515 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|