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 <TemplateScanner.hxx>
21
22 #include <comphelper/processfactory.hxx>
23 #include <comphelper/documentconstants.hxx>
24
25 #include <sfx2/doctempl.hxx>
26 #include <com/sun/star/frame/DocumentTemplates.hpp>
27 #include <com/sun/star/frame/XDocumentTemplates.hpp>
28 #include <com/sun/star/ucb/XContentAccess.hpp>
29 #include <com/sun/star/sdbc/XResultSet.hpp>
30 #include <com/sun/star/sdbc/XRow.hpp>
31
32 #include <set>
33 #include <utility>
34
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::uno;
37
38 namespace {
39
40 constexpr OUString TITLE = u"Title"_ustr;
41
42 class FolderDescriptor
43 {
44 public:
FolderDescriptor(int nPriority,OUString sContentIdentifier,const Reference<css::ucb::XCommandEnvironment> & rxFolderEnvironment)45 FolderDescriptor (
46 int nPriority,
47 OUString sContentIdentifier,
48 const Reference<css::ucb::XCommandEnvironment>& rxFolderEnvironment)
49 : mnPriority(nPriority),
50 msContentIdentifier(std::move(sContentIdentifier)),
51 mxFolderEnvironment(rxFolderEnvironment)
52 { }
53 int mnPriority;
54 OUString msContentIdentifier;
55 // Reference<sdbc::XResultSet> mxFolderResultSet;
56 Reference<css::ucb::XCommandEnvironment> mxFolderEnvironment;
57
58 class Comparator
59 {
60 public:
operator ()(const FolderDescriptor & r1,const FolderDescriptor & r2) const61 bool operator() (const FolderDescriptor& r1, const FolderDescriptor& r2) const
62 { return r1.mnPriority < r2.mnPriority; }
63 };
64 };
65
66 /** Use a heuristic based on the URL of a top-level template folder to
67 assign a priority that is used to sort the folders.
68 */
Classify(std::u16string_view rsURL)69 int Classify (std::u16string_view rsURL)
70 {
71 int nPriority (0);
72
73 if (rsURL.empty())
74 nPriority = 100;
75 else if (rsURL.find(u"presnt") != std::u16string_view::npos)
76 {
77 nPriority = 30;
78 }
79 else if (rsURL.find(u"layout") != std::u16string_view::npos)
80 {
81 nPriority = 20;
82 }
83 else if (rsURL.find(u"educate") != std::u16string_view::npos)
84 {
85 nPriority = 40;
86 }
87 else if (rsURL.find(u"finance") != std::u16string_view::npos)
88 {
89 nPriority = 40;
90 }
91 else
92 {
93 // All other folders are taken for user supplied and have the
94 // highest priority.
95 nPriority = 10;
96 }
97
98 return nPriority;
99 }
100
101 } // end of anonymous namespace
102
103 namespace sd
104 {
105
106 class TemplateScanner::FolderDescriptorList
107 : public ::std::multiset<FolderDescriptor,FolderDescriptor::Comparator>
108 {
109 };
110
TemplateScanner()111 TemplateScanner::TemplateScanner()
112 : meState(INITIALIZE_SCANNING),
113 mpFolderDescriptors(new FolderDescriptorList)
114 {
115 // empty;
116 }
117
~TemplateScanner()118 TemplateScanner::~TemplateScanner()
119 {
120 }
121
GetTemplateRoot()122 TemplateScanner::State TemplateScanner::GetTemplateRoot()
123 {
124 const Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
125 Reference<frame::XDocumentTemplates> xTemplates = frame::DocumentTemplates::create(xContext);
126 mxTemplateRoot = xTemplates->getContent();
127
128 return INITIALIZE_FOLDER_SCANNING;
129 }
130
InitializeEntryScanning()131 TemplateScanner::State TemplateScanner::InitializeEntryScanning()
132 {
133 State eNextState (SCAN_ENTRY);
134
135 if (maFolderContent.isFolder())
136 {
137 mxEntryEnvironment.clear();
138
139 // Create a cursor to iterate over the templates in this folders.
140 // We are interested only in three properties: the entry's name,
141 // its URL, and its content type.
142 mxEntryResultSet.set( maFolderContent.createCursor({ TITLE, u"TargetURL"_ustr, u"TypeDescription"_ustr }, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY));
143 }
144 else
145 eNextState = ERROR;
146
147 return eNextState;
148 }
149
ScanEntry()150 TemplateScanner::State TemplateScanner::ScanEntry()
151 {
152 State eNextState (ERROR);
153
154 Reference<css::ucb::XContentAccess> xContentAccess (mxEntryResultSet, UNO_QUERY);
155 Reference<css::sdbc::XRow> xRow (mxEntryResultSet, UNO_QUERY);
156
157 if (xContentAccess.is() && xRow.is() && mxEntryResultSet.is())
158 {
159 if (mxEntryResultSet->next())
160 {
161 OUString sTitle (xRow->getString (1));
162 OUString sTargetURL (xRow->getString (2));
163 OUString sContentType (xRow->getString (3));
164
165 OUString aId = xContentAccess->queryContentIdentifierString();
166 ::ucbhelper::Content aContent(aId, mxEntryEnvironment, comphelper::getProcessComponentContext());
167 if (aContent.isDocument ())
168 {
169 // Check whether the entry is an impress template. If so
170 // add a new entry to the resulting list (which is created
171 // first if necessary).
172 // These strings are used to find impress templates in the tree of
173 // template files. Should probably be determined dynamically.
174 if ( (sContentType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII)
175 || (sContentType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII)
176 || (sContentType == "application/vnd.stardivision.impress")
177 || (sContentType == MIMETYPE_VND_SUN_XML_IMPRESS_ASCII)
178 // The following id comes from the bugdoc in #i2764#.
179 || (sContentType == "Impress 2.0"))
180 {
181 OUString sLocalisedTitle = SfxDocumentTemplates::ConvertResourceString(sTitle);
182 mpTemplateEntries.push_back(std::make_unique<TemplateEntry>(sLocalisedTitle, sTargetURL));
183 }
184 }
185
186 // Continue scanning entries.
187 eNextState = SCAN_ENTRY;
188 }
189 else
190 {
191 // Continue with scanning the next folder.
192 eNextState = SCAN_FOLDER;
193 }
194 }
195
196 return eNextState;
197 }
198
InitializeFolderScanning()199 TemplateScanner::State TemplateScanner::InitializeFolderScanning()
200 {
201 State eNextState (ERROR);
202
203 mxFolderResultSet.clear();
204
205 try
206 {
207 // Create content for template folders.
208 mxFolderEnvironment.clear();
209 ::ucbhelper::Content aTemplateDir (mxTemplateRoot, mxFolderEnvironment, comphelper::getProcessComponentContext());
210
211 // Create a cursor to iterate over the template folders.
212 mxFolderResultSet.set( aTemplateDir.createCursor({ TITLE, u"TargetDirURL"_ustr }, ::ucbhelper::INCLUDE_FOLDERS_ONLY));
213 if (mxFolderResultSet.is())
214 eNextState = GATHER_FOLDER_LIST;
215 }
216 catch (css::uno::Exception&)
217 {
218 eNextState = ERROR;
219 }
220
221 return eNextState;
222 }
223
GatherFolderList()224 TemplateScanner::State TemplateScanner::GatherFolderList()
225 {
226 State eNextState (ERROR);
227
228 Reference<css::ucb::XContentAccess> xContentAccess (mxFolderResultSet, UNO_QUERY);
229 if (xContentAccess.is() && mxFolderResultSet.is())
230 {
231 while (mxFolderResultSet->next())
232 {
233 Reference<sdbc::XRow> xRow (mxFolderResultSet, UNO_QUERY);
234 if (xRow.is())
235 {
236 OUString sTargetDir (xRow->getString (2));
237
238 mpFolderDescriptors->insert(
239 FolderDescriptor(
240 Classify(sTargetDir),
241 xContentAccess->queryContentIdentifierString(),
242 mxFolderEnvironment));
243 }
244 }
245
246 eNextState = SCAN_FOLDER;
247 }
248
249 return eNextState;
250 }
251
ScanFolder()252 TemplateScanner::State TemplateScanner::ScanFolder()
253 {
254 State eNextState (ERROR);
255
256 if (!mpFolderDescriptors->empty())
257 {
258 FolderDescriptor aDescriptor (*mpFolderDescriptors->begin());
259 mpFolderDescriptors->erase(mpFolderDescriptors->begin());
260
261 OUString aId (aDescriptor.msContentIdentifier);
262
263 maFolderContent = ::ucbhelper::Content (aId, aDescriptor.mxFolderEnvironment, comphelper::getProcessComponentContext());
264 if (maFolderContent.isFolder())
265 {
266 // Scan the folder and insert it into the list of template
267 // folders.
268 // Continue with scanning all entries in the folder.
269 mpTemplateEntries.clear();
270 eNextState = INITIALIZE_ENTRY_SCAN;
271 }
272 }
273 else
274 {
275 eNextState = DONE;
276 }
277
278 return eNextState;
279 }
280
RunNextStep()281 void TemplateScanner::RunNextStep()
282 {
283 switch (meState)
284 {
285 case INITIALIZE_SCANNING:
286 meState = GetTemplateRoot();
287 break;
288
289 case INITIALIZE_FOLDER_SCANNING:
290 meState = InitializeFolderScanning();
291 break;
292
293 case SCAN_FOLDER:
294 meState = ScanFolder();
295 break;
296
297 case GATHER_FOLDER_LIST:
298 meState = GatherFolderList();
299 break;
300
301 case INITIALIZE_ENTRY_SCAN:
302 meState = InitializeEntryScanning();
303 break;
304
305 case SCAN_ENTRY:
306 meState = ScanEntry();
307 break;
308 default:
309 break;
310 }
311
312 switch (meState)
313 {
314 case DONE:
315 case ERROR:
316 mxTemplateRoot.clear();
317 mxFolderEnvironment.clear();
318 mxEntryEnvironment.clear();
319 mxFolderResultSet.clear();
320 mxEntryResultSet.clear();
321 break;
322 default:
323 break;
324 }
325 }
326
HasNextStep()327 bool TemplateScanner::HasNextStep()
328 {
329 switch (meState)
330 {
331 case DONE:
332 case ERROR:
333 return false;
334
335 default:
336 return true;
337 }
338 }
339
340 }
341
342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
343