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