1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.lar;
24  
25  import com.liferay.portal.LARFileException;
26  import com.liferay.portal.LARTypeException;
27  import com.liferay.portal.LayoutImportException;
28  import com.liferay.portal.NoSuchPortletPreferencesException;
29  import com.liferay.portal.PortalException;
30  import com.liferay.portal.PortletIdException;
31  import com.liferay.portal.SystemException;
32  import com.liferay.portal.kernel.util.GetterUtil;
33  import com.liferay.portal.kernel.util.ObjectValuePair;
34  import com.liferay.portal.kernel.util.PortletClassInvoker;
35  import com.liferay.portal.kernel.util.ReleaseInfo;
36  import com.liferay.portal.kernel.util.StringUtil;
37  import com.liferay.portal.kernel.util.Validator;
38  import com.liferay.portal.kernel.xml.Document;
39  import com.liferay.portal.kernel.xml.DocumentException;
40  import com.liferay.portal.kernel.xml.Element;
41  import com.liferay.portal.kernel.xml.SAXReaderUtil;
42  import com.liferay.portal.kernel.zip.ZipReader;
43  import com.liferay.portal.model.Layout;
44  import com.liferay.portal.model.Portlet;
45  import com.liferay.portal.model.PortletConstants;
46  import com.liferay.portal.model.PortletItem;
47  import com.liferay.portal.model.PortletPreferences;
48  import com.liferay.portal.model.User;
49  import com.liferay.portal.service.LayoutLocalServiceUtil;
50  import com.liferay.portal.service.PortletItemLocalServiceUtil;
51  import com.liferay.portal.service.PortletLocalServiceUtil;
52  import com.liferay.portal.service.PortletPreferencesLocalServiceUtil;
53  import com.liferay.portal.service.UserLocalServiceUtil;
54  import com.liferay.portal.service.persistence.PortletPreferencesUtil;
55  import com.liferay.portal.service.persistence.UserUtil;
56  import com.liferay.portal.util.PortletKeys;
57  import com.liferay.portlet.PortletPreferencesImpl;
58  import com.liferay.portlet.PortletPreferencesSerializer;
59  import com.liferay.portlet.messageboards.model.MBMessage;
60  import com.liferay.portlet.ratings.model.RatingsEntry;
61  import com.liferay.util.MapUtil;
62  
63  import java.io.InputStream;
64  
65  import java.util.ArrayList;
66  import java.util.HashSet;
67  import java.util.List;
68  import java.util.Map;
69  
70  import org.apache.commons.lang.time.StopWatch;
71  import org.apache.commons.logging.Log;
72  import org.apache.commons.logging.LogFactory;
73  
74  /**
75   * <a href="PortletImporter.java.html"><b><i>View Source</i></b></a>
76   *
77   * @author Brian Wing Shun Chan
78   * @author Joel Kozikowski
79   * @author Charles May
80   * @author Raymond Augé
81   * @author Jorge Ferrer
82   * @author Bruno Farache
83   *
84   */
85  public class PortletImporter {
86  
87      public void importPortletInfo(
88              long userId, long plid, String portletId,
89              Map<String, String[]> parameterMap, InputStream is)
90          throws PortalException, SystemException {
91  
92          boolean deletePortletData = MapUtil.getBoolean(
93              parameterMap, PortletDataHandlerKeys.DELETE_PORTLET_DATA);
94          boolean importPortletData = MapUtil.getBoolean(
95              parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
96          boolean importPortletArchivedSetups = MapUtil.getBoolean(
97              parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
98          boolean importPortletSetup = MapUtil.getBoolean(
99              parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
100         boolean importUserPreferences = MapUtil.getBoolean(
101             parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
102         String userIdStrategy = MapUtil.getString(
103             parameterMap, PortletDataHandlerKeys.USER_ID_STRATEGY);
104 
105         StopWatch stopWatch = null;
106 
107         if (_log.isInfoEnabled()) {
108             stopWatch = new StopWatch();
109 
110             stopWatch.start();
111         }
112 
113         Layout layout = LayoutLocalServiceUtil.getLayout(plid);
114 
115         long companyId = layout.getCompanyId();
116 
117         User user = UserUtil.findByPrimaryKey(userId);
118 
119         UserIdStrategy strategy = getUserIdStrategy(user, userIdStrategy);
120 
121         ZipReader zipReader = new ZipReader(is);
122 
123         PortletDataContext context = new PortletDataContextImpl(
124             companyId, layout.getGroupId(), parameterMap, new HashSet<String>(),
125             strategy, zipReader);
126 
127         context.setPlid(plid);
128 
129         // Zip
130 
131         Element root = null;
132 
133         // Manifest
134 
135         String xml = context.getZipEntryAsString("/manifest.xml");
136 
137         try {
138             Document doc = SAXReaderUtil.read(xml);
139 
140             root = doc.getRootElement();
141         }
142         catch (Exception e) {
143             throw new LARFileException(
144                 "Cannot locate a manifest in this LAR file.");
145         }
146 
147         // Build compatibility
148 
149         Element header = root.element("header");
150 
151         int buildNumber = ReleaseInfo.getBuildNumber();
152 
153         int importBuildNumber = GetterUtil.getInteger(
154             header.attributeValue("build-number"));
155 
156         if (buildNumber != importBuildNumber) {
157             throw new LayoutImportException(
158                 "LAR build number " + importBuildNumber + " does not match " +
159                     "portal build number " + buildNumber);
160         }
161 
162         // Type compatibility
163 
164         String type = header.attributeValue("type");
165 
166         if (!type.equals("portlet")) {
167             throw new LARTypeException(
168                 "Invalid type of LAR file (" + type + ")");
169         }
170 
171         // Portlet compatibility
172 
173         String rootPortletId = header.attributeValue("root-portlet-id");
174 
175         if (!PortletConstants.getRootPortletId(portletId).equals(
176                 rootPortletId)) {
177 
178             throw new PortletIdException("Invalid portlet id " + rootPortletId);
179         }
180 
181         // Import GroupId
182 
183         long importGroupId = GetterUtil.getLong(
184             header.attributeValue("group-id"));
185 
186         context.setImportGroupId(importGroupId);
187 
188         // Read comments, ratings, and tags to make them available to the data
189         // handlers through the context
190 
191         readComments(context, root);
192         readRatings(context, root);
193         readTags(context, root);
194 
195         // Delete portlet data
196 
197         if (_log.isDebugEnabled()) {
198             _log.debug("Deleting portlet data");
199         }
200 
201         if (deletePortletData) {
202             deletePortletData(context, portletId, plid);
203         }
204 
205         Element portletRefEl = root.element("portlet");
206         Element portletEl = null;
207 
208         try {
209             Document portletDoc = SAXReaderUtil.read(
210                 context.getZipEntryAsString(
211                     portletRefEl.attributeValue("path")));
212 
213             portletEl = portletDoc.getRootElement();
214         }
215         catch (DocumentException de) {
216             throw new SystemException(de);
217         }
218 
219         // Portlet preferences
220 
221         importPortletPreferences(
222             context, layout.getCompanyId(), layout.getGroupId(), plid,
223             portletId, portletEl, importPortletSetup,
224             importPortletArchivedSetups, importUserPreferences);
225 
226         // Portlet data
227 
228         if (_log.isDebugEnabled()) {
229             _log.debug("Importing portlet data");
230         }
231 
232         if (importPortletData) {
233             importPortletData(
234                 context, portletId, plid, portletEl.element("portlet-data"));
235         }
236 
237         if (_log.isInfoEnabled()) {
238             _log.info(
239                 "Importing portlet data takes " + stopWatch.getTime() + " ms");
240         }
241     }
242 
243     protected void deletePortletData(
244             PortletDataContext context, String portletId, long plid)
245         throws SystemException {
246 
247         try {
248             long ownerId = PortletKeys.PREFS_OWNER_ID_DEFAULT;
249             int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
250 
251             PortletPreferences portletPreferences =
252                 PortletPreferencesUtil.findByO_O_P_P(
253                     ownerId, ownerType, plid, portletId);
254 
255             String xml = deletePortletData(
256                 context, portletId, portletPreferences);
257 
258             if (xml != null) {
259                 PortletPreferencesLocalServiceUtil.updatePreferences(
260                     ownerId, ownerType, plid, portletId, xml);
261             }
262         }
263         catch (NoSuchPortletPreferencesException nsppe) {
264         }
265     }
266 
267     protected String deletePortletData(
268             PortletDataContext context, String portletId,
269             PortletPreferences portletPreferences)
270         throws SystemException {
271 
272         Portlet portlet = PortletLocalServiceUtil.getPortletById(
273             context.getCompanyId(), portletId);
274 
275         if (portlet == null) {
276             if (_log.isDebugEnabled()) {
277                 _log.debug(
278                     "Do not delete portlet data for " + portletId +
279                         " because the portlet does not exist");
280             }
281 
282             return null;
283         }
284 
285         String portletDataHandlerClass =
286             portlet.getPortletDataHandlerClass();
287 
288         if (Validator.isNull(portletDataHandlerClass)) {
289             if (_log.isDebugEnabled()) {
290                 _log.debug(
291                     "Do not delete portlet data for " + portletId +
292                         " because the portlet does not have a " +
293                             "PortletDataHandler");
294             }
295 
296             return null;
297         }
298 
299         if (_log.isDebugEnabled()) {
300             _log.debug("Deleting data for " + portletId);
301         }
302 
303         PortletPreferencesImpl prefsImpl =
304             (PortletPreferencesImpl)PortletPreferencesSerializer.fromDefaultXML(
305                 portletPreferences.getPreferences());
306 
307         try {
308             prefsImpl = (PortletPreferencesImpl)PortletClassInvoker.invoke(
309                 portletId, portletDataHandlerClass, "deleteData", context,
310                 portletId, prefsImpl);
311         }
312         catch (Exception e) {
313             throw new SystemException(e);
314         }
315 
316         if (prefsImpl == null) {
317             return null;
318         }
319 
320         return PortletPreferencesSerializer.toXML(prefsImpl);
321     }
322 
323     protected UserIdStrategy getUserIdStrategy(
324         User user, String userIdStrategy) {
325 
326         if (UserIdStrategy.ALWAYS_CURRENT_USER_ID.equals(userIdStrategy)) {
327             return new AlwaysCurrentUserIdStrategy(user);
328         }
329 
330         return new CurrentUserIdStrategy(user);
331     }
332 
333     protected void importPortletData(
334             PortletDataContext context, String portletId, long plid,
335             Element portletDataRefEl)
336         throws SystemException {
337 
338         try {
339             long ownerId = PortletKeys. PREFS_OWNER_ID_DEFAULT;
340             int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
341 
342             PortletPreferences portletPreferences =
343                 PortletPreferencesUtil.findByO_O_P_P(
344                     ownerId, ownerType, plid, portletId);
345 
346             String xml = importPortletData(
347                 context, portletId, portletPreferences, portletDataRefEl);
348 
349             if (xml != null) {
350                 PortletPreferencesLocalServiceUtil.updatePreferences(
351                     ownerId, ownerType, plid, portletId, xml);
352             }
353         }
354         catch (NoSuchPortletPreferencesException nsppe) {
355         }
356     }
357 
358     protected String importPortletData(
359             PortletDataContext context, String portletId,
360             PortletPreferences portletPreferences, Element portletDataRefEl)
361         throws SystemException {
362 
363         Portlet portlet = PortletLocalServiceUtil.getPortletById(
364             context.getCompanyId(), portletId);
365 
366         if (portlet == null) {
367             if (_log.isDebugEnabled()) {
368                 _log.debug(
369                     "Do not import portlet data for " + portletId +
370                         " because the portlet does not exist");
371             }
372 
373             return null;
374         }
375 
376         String portletDataHandlerClass =
377             portlet.getPortletDataHandlerClass();
378 
379         if (Validator.isNull(portletDataHandlerClass)) {
380             if (_log.isDebugEnabled()) {
381                 _log.debug(
382                     "Do not import portlet data for " + portletId +
383                         " because the portlet does not have a " +
384                             "PortletDataHandler");
385             }
386 
387             return null;
388         }
389 
390         if (_log.isDebugEnabled()) {
391             _log.debug("Importing data for " + portletId);
392         }
393 
394         PortletPreferencesImpl prefsImpl = null;
395 
396         if (portletPreferences != null) {
397             prefsImpl = (PortletPreferencesImpl)
398                 PortletPreferencesSerializer.fromDefaultXML(
399                     portletPreferences.getPreferences());
400         }
401 
402         String portletData = context.getZipEntryAsString(
403             portletDataRefEl.attributeValue("path"));
404 
405         try {
406             prefsImpl = (PortletPreferencesImpl)PortletClassInvoker.invoke(
407                 portletId, portletDataHandlerClass, "importData", context,
408                 portletId, prefsImpl, portletData);
409         }
410         catch (Exception e) {
411             throw new SystemException(e);
412         }
413 
414         if (prefsImpl == null) {
415             return null;
416         }
417 
418         return PortletPreferencesSerializer.toXML(prefsImpl);
419     }
420 
421     protected void importPortletPreferences(
422             PortletDataContext context, long companyId, long groupId, long plid,
423             String portletId, Element parentEl, boolean importPortletSetup,
424             boolean importPortletArchivedSetups, boolean importUserPreferences)
425         throws PortalException, SystemException {
426 
427         long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
428 
429         List<Element> prefsEls = parentEl.elements("portlet-preferences");
430 
431         for (Element prefEl : prefsEls) {
432             String path = prefEl.attributeValue("path");
433 
434             if (context.isPathNotProcessed(path)) {
435                 Element el = null;
436                 String xml = null;
437 
438                 try {
439                     xml = context.getZipEntryAsString(path);
440 
441                     Document prefsDoc = SAXReaderUtil.read(xml);
442 
443                     el = prefsDoc.getRootElement();
444                 }
445                 catch (DocumentException de) {
446                     throw new SystemException(de);
447                 }
448 
449                 long ownerId = GetterUtil.getLong(
450                     el.attributeValue("owner-id"));
451                 int ownerType = GetterUtil.getInteger(
452                     el.attributeValue("owner-type"));
453 
454                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_COMPANY) {
455                     continue;
456                 }
457 
458                 if (((ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) ||
459                      (ownerType == PortletKeys.PREFS_OWNER_TYPE_LAYOUT)) &&
460                     !importPortletSetup) {
461 
462                     continue;
463                 }
464 
465                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) &&
466                     !importPortletArchivedSetups) {
467 
468                     continue;
469                 }
470 
471                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_USER) &&
472                     (ownerId != PortletKeys.PREFS_OWNER_ID_DEFAULT) &&
473                     !importUserPreferences) {
474 
475                     continue;
476                 }
477 
478                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) {
479                     plid = PortletKeys.PREFS_PLID_SHARED;
480                     ownerId = context.getGroupId();
481                 }
482 
483                 boolean defaultUser = GetterUtil.getBoolean(
484                     el.attributeValue("default-user"));
485 
486                 if (portletId == null) {
487                     portletId = el.attributeValue("portlet-id");
488                 }
489 
490                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) {
491                     String userUuid = el.attributeValue("archive-user-uuid");
492                     String name = el.attributeValue("archive-name");
493 
494                     long userId = context.getUserId(userUuid);
495 
496                     PortletItem portletItem =
497                         PortletItemLocalServiceUtil.updatePortletItem(
498                             userId, groupId, name, portletId,
499                             PortletPreferences.class.getName());
500 
501                     plid = 0;
502                     ownerId = portletItem.getPortletItemId();
503                 }
504 
505                 if (defaultUser) {
506                     ownerId = defaultUserId;
507                 }
508 
509                 PortletPreferencesLocalServiceUtil.updatePreferences(
510                     ownerId, ownerType, plid, portletId, xml);
511             }
512         }
513     }
514 
515     protected void readComments(PortletDataContext context, Element parentEl)
516         throws SystemException {
517 
518         try {
519             String xml = context.getZipEntryAsString(
520                 context.getImportRootPath() + "/comments.xml");
521 
522             Document doc = SAXReaderUtil.read(xml);
523 
524             Element root = doc.getRootElement();
525 
526             List<Element> assets = root.elements("asset");
527 
528             for (Element asset : assets) {
529                 String path = asset.attributeValue("path");
530                 String className = asset.attributeValue("class-name");
531                 long classPK = GetterUtil.getLong(
532                     asset.attributeValue("class-pk"));
533 
534                 List<ObjectValuePair<String, byte[]>> entries =
535                     context.getZipFolderEntries(path);
536 
537                 List<MBMessage> messages = new ArrayList<MBMessage>();
538 
539                 for (ObjectValuePair<String, byte[]> entry : entries) {
540                     if (entry.getValue().length > 0) {
541                         MBMessage message = (MBMessage)context.fromXML(
542                             entry.getValue());
543 
544                         messages.add(message);
545                     }
546                 }
547 
548                 context.addComments(className, classPK, messages);
549             }
550         }
551         catch (Exception e) {
552             throw new SystemException(e);
553         }
554     }
555 
556     protected void readRatings(PortletDataContext context, Element parentEl)
557         throws SystemException {
558 
559         try {
560             String xml = context.getZipEntryAsString(
561                 context.getImportRootPath() + "/ratings.xml");
562 
563             Document doc = SAXReaderUtil.read(xml);
564 
565             Element root = doc.getRootElement();
566 
567             List<Element> assets = root.elements("asset");
568 
569             for (Element asset : assets) {
570                 String path = asset.attributeValue("path");
571                 String className = asset.attributeValue("class-name");
572                 long classPK = GetterUtil.getLong(
573                     asset.attributeValue("class-pk"));
574 
575                 List<ObjectValuePair<String, byte[]>> entries =
576                     context.getZipFolderEntries(path);
577 
578                 List<RatingsEntry> ratings = new ArrayList<RatingsEntry>();
579 
580                 for (ObjectValuePair<String, byte[]> entry : entries) {
581                     if (entry.getValue().length > 0) {
582                         RatingsEntry rating = (RatingsEntry)context.fromXML(
583                             entry.getValue());
584 
585                         ratings.add(rating);
586                     }
587                 }
588 
589                 context.addRatingsEntries(
590                     className, new Long(classPK), ratings);
591             }
592         }
593         catch (Exception e) {
594             throw new SystemException(e);
595         }
596     }
597 
598     protected void readTags(PortletDataContext context, Element parentEl)
599         throws SystemException {
600 
601         try {
602             String xml = context.getZipEntryAsString(
603                 context.getImportRootPath() + "/tags.xml");
604 
605             Document doc = SAXReaderUtil.read(xml);
606 
607             Element root = doc.getRootElement();
608 
609             List<Element> assets = root.elements("asset");
610 
611             for (Element asset : assets) {
612                 String className = GetterUtil.getString(
613                     asset.attributeValue("class-name"));
614                 long classPK = GetterUtil.getLong(
615                     asset.attributeValue("class-pk"));
616                 String entries = GetterUtil.getString(
617                     asset.attributeValue("entries"));
618 
619                 context.addTagsEntries(
620                     className, new Long(classPK),
621                     StringUtil.split(entries, ","));
622             }
623         }
624         catch (Exception e) {
625             throw new SystemException(e);
626         }
627     }
628 
629     private static Log _log = LogFactory.getLog(PortletImporter.class);
630 
631 }