1   /**
2    * Copyright (c) 2000-2009 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.PortalException;
29  import com.liferay.portal.PortletIdException;
30  import com.liferay.portal.SystemException;
31  import com.liferay.portal.kernel.log.Log;
32  import com.liferay.portal.kernel.log.LogFactoryUtil;
33  import com.liferay.portal.kernel.util.GetterUtil;
34  import com.liferay.portal.kernel.util.MapUtil;
35  import com.liferay.portal.kernel.util.ObjectValuePair;
36  import com.liferay.portal.kernel.util.ReleaseInfo;
37  import com.liferay.portal.kernel.util.StringUtil;
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.portlet.social.util.SocialActivityThreadLocal;
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  
72  /**
73   * <a href="PortletImporter.java.html"><b><i>View Source</i></b></a>
74   *
75   * @author Brian Wing Shun Chan
76   * @author Joel Kozikowski
77   * @author Charles May
78   * @author Raymond Augé
79   * @author Jorge Ferrer
80   * @author Bruno Farache
81   *
82   */
83  public class PortletImporter {
84  
85      public void importPortletInfo(
86              long userId, long plid, long groupId, String portletId,
87              Map<String, String[]> parameterMap, InputStream is)
88          throws PortalException, SystemException {
89  
90          boolean deletePortletData = MapUtil.getBoolean(
91              parameterMap, PortletDataHandlerKeys.DELETE_PORTLET_DATA);
92          boolean importPortletData = MapUtil.getBoolean(
93              parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
94          boolean importPortletArchivedSetups = MapUtil.getBoolean(
95              parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
96          boolean importPortletSetup = MapUtil.getBoolean(
97              parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
98          boolean importUserPreferences = MapUtil.getBoolean(
99              parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
100         String userIdStrategy = MapUtil.getString(
101             parameterMap, PortletDataHandlerKeys.USER_ID_STRATEGY);
102 
103         StopWatch stopWatch = null;
104 
105         if (_log.isInfoEnabled()) {
106             stopWatch = new StopWatch();
107 
108             stopWatch.start();
109         }
110 
111         Layout layout = LayoutLocalServiceUtil.getLayout(plid);
112 
113         long companyId = layout.getCompanyId();
114 
115         User user = UserUtil.findByPrimaryKey(userId);
116 
117         UserIdStrategy strategy = getUserIdStrategy(user, userIdStrategy);
118 
119         ZipReader zipReader = new ZipReader(is);
120 
121         PortletDataContext context = new PortletDataContextImpl(
122             companyId, groupId, parameterMap, new HashSet<String>(), strategy,
123             zipReader);
124 
125         context.setPlid(plid);
126 
127         // Zip
128 
129         Element root = null;
130 
131         // Manifest
132 
133         String xml = context.getZipEntryAsString("/manifest.xml");
134 
135         try {
136             Document doc = SAXReaderUtil.read(xml);
137 
138             root = doc.getRootElement();
139         }
140         catch (Exception e) {
141             throw new LARFileException(
142                 "Cannot locate a manifest in this LAR file.");
143         }
144 
145         // Build compatibility
146 
147         Element header = root.element("header");
148 
149         int buildNumber = ReleaseInfo.getBuildNumber();
150 
151         int importBuildNumber = GetterUtil.getInteger(
152             header.attributeValue("build-number"));
153 
154         if (buildNumber != importBuildNumber) {
155             throw new LayoutImportException(
156                 "LAR build number " + importBuildNumber + " does not match " +
157                     "portal build number " + buildNumber);
158         }
159 
160         // Type compatibility
161 
162         String type = header.attributeValue("type");
163 
164         if (!type.equals("portlet")) {
165             throw new LARTypeException(
166                 "Invalid type of LAR file (" + type + ")");
167         }
168 
169         // Portlet compatibility
170 
171         String rootPortletId = header.attributeValue("root-portlet-id");
172 
173         if (!PortletConstants.getRootPortletId(portletId).equals(
174                 rootPortletId)) {
175 
176             throw new PortletIdException("Invalid portlet id " + rootPortletId);
177         }
178 
179         // Import GroupId
180 
181         long importGroupId = GetterUtil.getLong(
182             header.attributeValue("group-id"));
183 
184         context.setImportGroupId(importGroupId);
185 
186         // Read comments, ratings, and tags to make them available to the data
187         // handlers through the context
188 
189         readComments(context, root);
190         readRatings(context, root);
191         readTags(context, root);
192 
193         // Delete portlet data
194 
195         if (_log.isDebugEnabled()) {
196             _log.debug("Deleting portlet data");
197         }
198 
199         if (deletePortletData) {
200             deletePortletData(context, portletId, plid);
201         }
202 
203         Element portletRefEl = root.element("portlet");
204         Element portletEl = null;
205 
206         try {
207             Document portletDoc = SAXReaderUtil.read(
208                 context.getZipEntryAsString(
209                     portletRefEl.attributeValue("path")));
210 
211             portletEl = portletDoc.getRootElement();
212         }
213         catch (DocumentException de) {
214             throw new SystemException(de);
215         }
216 
217         // Portlet preferences
218 
219         importPortletPreferences(
220             context, layout.getCompanyId(), groupId, plid, portletId, portletEl,
221             importPortletSetup, importPortletArchivedSetups,
222             importUserPreferences);
223 
224         // Portlet data
225 
226         if (_log.isDebugEnabled()) {
227             _log.debug("Importing portlet data");
228         }
229 
230         if (importPortletData) {
231             Element portletDataRefEl = portletEl.element("portlet-data");
232 
233             if (portletDataRefEl != null) {
234                 importPortletData(
235                     context, portletId, plid, portletDataRefEl);
236             }
237             else {
238                 _log.warn(
239                     "Could not import portlet data because it cannot be " +
240                         "found in the input");
241             }
242         }
243 
244         if (_log.isInfoEnabled()) {
245             _log.info(
246                 "Importing portlet data takes " + stopWatch.getTime() + " ms");
247         }
248     }
249 
250     protected void deletePortletData(
251             PortletDataContext context, String portletId, long plid)
252         throws SystemException {
253 
254         long ownerId = PortletKeys.PREFS_OWNER_ID_DEFAULT;
255         int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
256 
257         PortletPreferences portletPreferences =
258             PortletPreferencesUtil.fetchByO_O_P_P(
259                 ownerId, ownerType, plid, portletId);
260 
261         if (portletPreferences == null) {
262             portletPreferences =
263                 new com.liferay.portal.model.impl.PortletPreferencesImpl();
264         }
265 
266         String xml = deletePortletData(
267             context, portletId, portletPreferences);
268 
269         if (xml != null) {
270             PortletPreferencesLocalServiceUtil.updatePreferences(
271                 ownerId, ownerType, plid, portletId, xml);
272         }
273     }
274 
275     protected String deletePortletData(
276             PortletDataContext context, String portletId,
277             PortletPreferences portletPreferences)
278         throws SystemException {
279 
280         Portlet portlet = PortletLocalServiceUtil.getPortletById(
281             context.getCompanyId(), portletId);
282 
283         if (portlet == null) {
284             if (_log.isDebugEnabled()) {
285                 _log.debug(
286                     "Do not delete portlet data for " + portletId +
287                         " because the portlet does not exist");
288             }
289 
290             return null;
291         }
292 
293         PortletDataHandler portletDataHandler =
294             portlet.getPortletDataHandlerInstance();
295 
296         if (portletDataHandler == null) {
297             if (_log.isDebugEnabled()) {
298                 _log.debug(
299                     "Do not delete portlet data for " + portletId +
300                         " because the portlet does not have a " +
301                             "PortletDataHandler");
302             }
303 
304             return null;
305         }
306 
307         if (_log.isDebugEnabled()) {
308             _log.debug("Deleting data for " + portletId);
309         }
310 
311         PortletPreferencesImpl prefsImpl =
312             (PortletPreferencesImpl)PortletPreferencesSerializer.fromDefaultXML(
313                 portletPreferences.getPreferences());
314 
315         try {
316             prefsImpl =
317                 (PortletPreferencesImpl)portletDataHandler.deleteData(
318                     context, portletId, prefsImpl);
319         }
320         catch (Exception e) {
321             throw new SystemException(e);
322         }
323 
324         if (prefsImpl == null) {
325             return null;
326         }
327 
328         return PortletPreferencesSerializer.toXML(prefsImpl);
329     }
330 
331     protected UserIdStrategy getUserIdStrategy(
332         User user, String userIdStrategy) {
333 
334         if (UserIdStrategy.ALWAYS_CURRENT_USER_ID.equals(userIdStrategy)) {
335             return new AlwaysCurrentUserIdStrategy(user);
336         }
337 
338         return new CurrentUserIdStrategy(user);
339     }
340 
341     protected void importPortletData(
342             PortletDataContext context, String portletId, long plid,
343             Element portletDataRefEl)
344         throws SystemException {
345 
346         long ownerId = PortletKeys.PREFS_OWNER_ID_DEFAULT;
347         int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
348 
349         PortletPreferences portletPreferences =
350             PortletPreferencesUtil.fetchByO_O_P_P(
351                 ownerId, ownerType, plid, portletId);
352 
353         if (portletPreferences == null) {
354             portletPreferences =
355                 new com.liferay.portal.model.impl.PortletPreferencesImpl();
356         }
357 
358         String xml = importPortletData(
359             context, portletId, portletPreferences, portletDataRefEl);
360 
361         if (xml != null) {
362             PortletPreferencesLocalServiceUtil.updatePreferences(
363                 ownerId, ownerType, plid, portletId, xml);
364         }
365     }
366 
367     protected String importPortletData(
368             PortletDataContext context, String portletId,
369             PortletPreferences portletPreferences, Element portletDataRefEl)
370         throws SystemException {
371 
372         Portlet portlet = PortletLocalServiceUtil.getPortletById(
373             context.getCompanyId(), portletId);
374 
375         if (portlet == null) {
376             if (_log.isDebugEnabled()) {
377                 _log.debug(
378                     "Do not import portlet data for " + portletId +
379                         " because the portlet does not exist");
380             }
381 
382             return null;
383         }
384 
385         PortletDataHandler portletDataHandler =
386             portlet.getPortletDataHandlerInstance();
387 
388         if (portletDataHandler == null) {
389             if (_log.isDebugEnabled()) {
390                 _log.debug(
391                     "Do not import portlet data for " + portletId +
392                         " because the portlet does not have a " +
393                             "PortletDataHandler");
394             }
395 
396             return null;
397         }
398 
399         if (_log.isDebugEnabled()) {
400             _log.debug("Importing data for " + portletId);
401         }
402 
403         PortletPreferencesImpl prefsImpl = null;
404 
405         if (portletPreferences != null) {
406             prefsImpl = (PortletPreferencesImpl)
407                 PortletPreferencesSerializer.fromDefaultXML(
408                     portletPreferences.getPreferences());
409         }
410 
411         String portletData = context.getZipEntryAsString(
412             portletDataRefEl.attributeValue("path"));
413 
414         try {
415             SocialActivityThreadLocal.setEnabled(false);
416 
417             prefsImpl =
418                 (PortletPreferencesImpl)portletDataHandler.importData(
419                     context, portletId, prefsImpl, portletData);
420         }
421         catch (Exception e) {
422             throw new SystemException(e);
423         }
424         finally {
425             SocialActivityThreadLocal.setEnabled(true);
426         }
427 
428         if (prefsImpl == null) {
429             return null;
430         }
431 
432         return PortletPreferencesSerializer.toXML(prefsImpl);
433     }
434 
435     protected void importPortletPreferences(
436             PortletDataContext context, long companyId, long groupId, long plid,
437             String portletId, Element parentEl, boolean importPortletSetup,
438             boolean importPortletArchivedSetups, boolean importUserPreferences)
439         throws PortalException, SystemException {
440 
441         long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
442 
443         List<Element> prefsEls = parentEl.elements("portlet-preferences");
444 
445         for (Element prefEl : prefsEls) {
446             String path = prefEl.attributeValue("path");
447 
448             if (context.isPathNotProcessed(path)) {
449                 Element el = null;
450                 String xml = null;
451 
452                 try {
453                     xml = context.getZipEntryAsString(path);
454 
455                     Document prefsDoc = SAXReaderUtil.read(xml);
456 
457                     el = prefsDoc.getRootElement();
458                 }
459                 catch (DocumentException de) {
460                     throw new SystemException(de);
461                 }
462 
463                 long ownerId = GetterUtil.getLong(
464                     el.attributeValue("owner-id"));
465                 int ownerType = GetterUtil.getInteger(
466                     el.attributeValue("owner-type"));
467 
468                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_COMPANY) {
469                     continue;
470                 }
471 
472                 if (((ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) ||
473                      (ownerType == PortletKeys.PREFS_OWNER_TYPE_LAYOUT)) &&
474                     !importPortletSetup) {
475 
476                     continue;
477                 }
478 
479                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) &&
480                     !importPortletArchivedSetups) {
481 
482                     continue;
483                 }
484 
485                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_USER) &&
486                     (ownerId != PortletKeys.PREFS_OWNER_ID_DEFAULT) &&
487                     !importUserPreferences) {
488 
489                     continue;
490                 }
491 
492                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) {
493                     plid = PortletKeys.PREFS_PLID_SHARED;
494                     ownerId = context.getGroupId();
495                 }
496 
497                 boolean defaultUser = GetterUtil.getBoolean(
498                     el.attributeValue("default-user"));
499 
500                 if (portletId == null) {
501                     portletId = el.attributeValue("portlet-id");
502                 }
503 
504                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) {
505                     String userUuid = el.attributeValue("archive-user-uuid");
506                     String name = el.attributeValue("archive-name");
507 
508                     long userId = context.getUserId(userUuid);
509 
510                     PortletItem portletItem =
511                         PortletItemLocalServiceUtil.updatePortletItem(
512                             userId, groupId, name, portletId,
513                             PortletPreferences.class.getName());
514 
515                     plid = 0;
516                     ownerId = portletItem.getPortletItemId();
517                 }
518 
519                 if (defaultUser) {
520                     ownerId = defaultUserId;
521                 }
522 
523                 PortletPreferencesLocalServiceUtil.updatePreferences(
524                     ownerId, ownerType, plid, portletId, xml);
525             }
526         }
527     }
528 
529     protected void readComments(PortletDataContext context, Element parentEl)
530         throws SystemException {
531 
532         try {
533             String xml = context.getZipEntryAsString(
534                 context.getImportRootPath() + "/comments.xml");
535 
536             Document doc = SAXReaderUtil.read(xml);
537 
538             Element root = doc.getRootElement();
539 
540             List<Element> assets = root.elements("asset");
541 
542             for (Element asset : assets) {
543                 String path = asset.attributeValue("path");
544                 String className = asset.attributeValue("class-name");
545                 long classPK = GetterUtil.getLong(
546                     asset.attributeValue("class-pk"));
547 
548                 List<ObjectValuePair<String, byte[]>> entries =
549                     context.getZipFolderEntries(path);
550 
551                 List<MBMessage> messages = new ArrayList<MBMessage>();
552 
553                 for (ObjectValuePair<String, byte[]> entry : entries) {
554                     if (entry.getValue().length > 0) {
555                         MBMessage message = (MBMessage)context.fromXML(
556                             entry.getValue());
557 
558                         messages.add(message);
559                     }
560                 }
561 
562                 context.addComments(className, classPK, messages);
563             }
564         }
565         catch (Exception e) {
566             throw new SystemException(e);
567         }
568     }
569 
570     protected void readRatings(PortletDataContext context, Element parentEl)
571         throws SystemException {
572 
573         try {
574             String xml = context.getZipEntryAsString(
575                 context.getImportRootPath() + "/ratings.xml");
576 
577             Document doc = SAXReaderUtil.read(xml);
578 
579             Element root = doc.getRootElement();
580 
581             List<Element> assets = root.elements("asset");
582 
583             for (Element asset : assets) {
584                 String path = asset.attributeValue("path");
585                 String className = asset.attributeValue("class-name");
586                 long classPK = GetterUtil.getLong(
587                     asset.attributeValue("class-pk"));
588 
589                 List<ObjectValuePair<String, byte[]>> entries =
590                     context.getZipFolderEntries(path);
591 
592                 List<RatingsEntry> ratings = new ArrayList<RatingsEntry>();
593 
594                 for (ObjectValuePair<String, byte[]> entry : entries) {
595                     if (entry.getValue().length > 0) {
596                         RatingsEntry rating = (RatingsEntry)context.fromXML(
597                             entry.getValue());
598 
599                         ratings.add(rating);
600                     }
601                 }
602 
603                 context.addRatingsEntries(
604                     className, new Long(classPK), ratings);
605             }
606         }
607         catch (Exception e) {
608             throw new SystemException(e);
609         }
610     }
611 
612     protected void readTags(PortletDataContext context, Element parentEl)
613         throws SystemException {
614 
615         try {
616             String xml = context.getZipEntryAsString(
617                 context.getImportRootPath() + "/tags.xml");
618 
619             Document doc = SAXReaderUtil.read(xml);
620 
621             Element root = doc.getRootElement();
622 
623             List<Element> assets = root.elements("asset");
624 
625             for (Element asset : assets) {
626                 String className = GetterUtil.getString(
627                     asset.attributeValue("class-name"));
628                 long classPK = GetterUtil.getLong(
629                     asset.attributeValue("class-pk"));
630                 String entries = GetterUtil.getString(
631                     asset.attributeValue("entries"));
632 
633                 context.addTagsEntries(
634                     className, new Long(classPK),
635                     StringUtil.split(entries, ","));
636             }
637         }
638         catch (Exception e) {
639             throw new SystemException(e);
640         }
641     }
642 
643     private static Log _log = LogFactoryUtil.getLog(PortletImporter.class);
644 
645 }