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