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.lar.PortletDataContext;
34  import com.liferay.portal.kernel.lar.PortletDataHandler;
35  import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
36  import com.liferay.portal.kernel.lar.UserIdStrategy;
37  import com.liferay.portal.kernel.util.GetterUtil;
38  import com.liferay.portal.kernel.util.ObjectValuePair;
39  import com.liferay.portal.kernel.util.ReleaseInfo;
40  import com.liferay.portal.kernel.util.StringUtil;
41  import com.liferay.portal.kernel.zip.ZipReader;
42  import com.liferay.portal.model.Layout;
43  import com.liferay.portal.model.Portlet;
44  import com.liferay.portal.model.PortletConstants;
45  import com.liferay.portal.model.PortletItem;
46  import com.liferay.portal.model.PortletPreferences;
47  import com.liferay.portal.model.User;
48  import com.liferay.portal.service.LayoutLocalServiceUtil;
49  import com.liferay.portal.service.PortletItemLocalServiceUtil;
50  import com.liferay.portal.service.PortletLocalServiceUtil;
51  import com.liferay.portal.service.PortletPreferencesLocalServiceUtil;
52  import com.liferay.portal.service.UserLocalServiceUtil;
53  import com.liferay.portal.service.persistence.PortletPreferencesUtil;
54  import com.liferay.portal.service.persistence.UserUtil;
55  import com.liferay.portal.util.DocumentUtil;
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  import org.dom4j.Document;
75  import org.dom4j.DocumentException;
76  import org.dom4j.Element;
77  
78  /**
79   * <a href="PortletImporter.java.html"><b><i>View Source</i></b></a>
80   *
81   * @author Brian Wing Shun Chan
82   * @author Joel Kozikowski
83   * @author Charles May
84   * @author Raymond Augé
85   * @author Jorge Ferrer
86   * @author Bruno Farache
87   *
88   */
89  public class PortletImporter {
90  
91      public void importPortletInfo(
92              long userId, long plid, String portletId,
93              Map<String, String[]> parameterMap, InputStream is)
94          throws PortalException, SystemException {
95  
96          boolean deletePortletData = MapUtil.getBoolean(
97              parameterMap, PortletDataHandlerKeys.DELETE_PORTLET_DATA);
98          boolean importPortletData = MapUtil.getBoolean(
99              parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
100         boolean importPortletArchivedSetups = MapUtil.getBoolean(
101             parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
102         boolean importPortletSetup = MapUtil.getBoolean(
103             parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
104         boolean importUserPreferences = MapUtil.getBoolean(
105             parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
106         String userIdStrategy = MapUtil.getString(
107             parameterMap, PortletDataHandlerKeys.USER_ID_STRATEGY);
108 
109         StopWatch stopWatch = null;
110 
111         if (_log.isInfoEnabled()) {
112             stopWatch = new StopWatch();
113 
114             stopWatch.start();
115         }
116 
117         Layout layout = LayoutLocalServiceUtil.getLayout(plid);
118 
119         long companyId = layout.getCompanyId();
120 
121         User user = UserUtil.findByPrimaryKey(userId);
122 
123         UserIdStrategy strategy = getUserIdStrategy(user, userIdStrategy);
124 
125         ZipReader zipReader = new ZipReader(is);
126 
127         PortletDataContext context = new PortletDataContextImpl(
128             companyId, layout.getGroupId(), parameterMap, new HashSet(),
129             strategy, zipReader);
130 
131         context.setPlid(plid);
132 
133         // Zip
134 
135         Element root = null;
136 
137         // Manifest
138 
139         String xml = context.getZipEntryAsString("/manifest.xml");
140 
141         try {
142             Document doc = DocumentUtil.readDocumentFromXML(xml);
143 
144             root = doc.getRootElement();
145         }
146         catch (Exception e) {
147             throw new LARFileException(
148                 "Cannot locate a manifest in this LAR file.");
149         }
150 
151         // Build compatibility
152 
153         Element header = root.element("header");
154 
155         int buildNumber = ReleaseInfo.getBuildNumber();
156 
157         int importBuildNumber = GetterUtil.getInteger(
158             header.attributeValue("build-number"));
159 
160         if (buildNumber != importBuildNumber) {
161             throw new LayoutImportException(
162                 "LAR build number " + importBuildNumber + " does not match " +
163                     "portal build number " + buildNumber);
164         }
165 
166         // Type compatibility
167 
168         String type = header.attributeValue("type");
169 
170         if (!type.equals("portlet")) {
171             throw new LARTypeException(
172                 "Invalid type of LAR file (" + type + ")");
173         }
174 
175         // Portlet compatibility
176 
177         String rootPortletId = header.attributeValue("root-portlet-id");
178 
179         if (!PortletConstants.getRootPortletId(portletId).equals(
180                 rootPortletId)) {
181 
182             throw new PortletIdException("Invalid portlet id " + rootPortletId);
183         }
184 
185         // Import GroupId
186 
187         long importGroupId = GetterUtil.getLong(
188             header.attributeValue("group-id"));
189 
190         context.setImportGroupId(importGroupId);
191 
192         // Read comments, ratings, and tags to make them available to the data
193         // handlers through the context
194 
195         readComments(context, root);
196         readRatings(context, root);
197         readTags(context, root);
198 
199         // Delete portlet data
200 
201         if (_log.isDebugEnabled()) {
202             _log.debug("Deleting portlet data");
203         }
204 
205         if (deletePortletData) {
206             deletePortletData(context, portletId, plid);
207         }
208 
209         Element portletRefEl = root.element("portlet");
210         Element portletEl = null;
211 
212         try {
213             Document portletDoc = DocumentUtil.readDocumentFromXML(
214                 context.getZipEntryAsString(
215                     portletRefEl.attributeValue("path")));
216 
217             portletEl = portletDoc.getRootElement();
218         }
219         catch (DocumentException de) {
220             throw new SystemException(de);
221         }
222 
223         // Portlet preferences
224 
225         importPortletPreferences(
226             context, layout.getCompanyId(), layout.getGroupId(), plid,
227             portletId, portletEl, importPortletSetup,
228             importPortletArchivedSetups, importUserPreferences);
229 
230         // Portlet data
231 
232         if (_log.isDebugEnabled()) {
233             _log.debug("Importing portlet data");
234         }
235 
236         if (importPortletData) {
237             importPortletData(
238                 context, portletId, plid, portletEl.element("portlet-data"));
239         }
240 
241         if (_log.isInfoEnabled()) {
242             _log.info(
243                 "Importing portlet data takes " + stopWatch.getTime() + " ms");
244         }
245     }
246 
247     protected void deletePortletData(
248             PortletDataContext context, String portletId, long plid)
249         throws PortalException, SystemException {
250 
251         try {
252             PortletPreferences portletPreferences =
253                 PortletPreferencesUtil.findByO_O_P_P(
254                     PortletKeys.PREFS_OWNER_ID_DEFAULT,
255                     PortletKeys.PREFS_OWNER_TYPE_LAYOUT, plid,
256                     portletId);
257 
258             String preferences = deletePortletData(
259                 context, portletId, portletPreferences);
260 
261             if (preferences != null) {
262                 portletPreferences.setPreferences(preferences);
263 
264                 PortletPreferencesUtil.update(portletPreferences, false);
265             }
266         }
267         catch (NoSuchPortletPreferencesException nsppe) {
268         }
269     }
270 
271     protected String deletePortletData(
272             PortletDataContext context, String portletId,
273             PortletPreferences portletPreferences)
274         throws PortalException, SystemException {
275 
276         Portlet portlet = PortletLocalServiceUtil.getPortletById(
277             context.getCompanyId(), portletId);
278 
279         if (portlet == null) {
280             if (_log.isDebugEnabled()) {
281                 _log.debug(
282                     "Do not delete portlet data for " + portletId +
283                         " because the portlet does not exist");
284             }
285 
286             return null;
287         }
288 
289         PortletDataHandler portletDataHandler =
290             portlet.getPortletDataHandlerInstance();
291 
292         if (portletDataHandler == null) {
293             if (_log.isDebugEnabled()) {
294                 _log.debug(
295                     "Do not delete portlet data for " + portletId +
296                         " because the portlet does not have a " +
297                             "PortletDataHandler");
298             }
299 
300             return null;
301         }
302 
303         if (_log.isDebugEnabled()) {
304             _log.debug("Deleting data for " + portletId);
305         }
306 
307         PortletPreferencesImpl prefsImpl =
308             (PortletPreferencesImpl)PortletPreferencesSerializer.fromDefaultXML(
309                 portletPreferences.getPreferences());
310 
311         prefsImpl = (PortletPreferencesImpl)portletDataHandler.deleteData(
312             context, portletId, prefsImpl);
313 
314         if (prefsImpl == null) {
315             return null;
316         }
317 
318         return PortletPreferencesSerializer.toXML(prefsImpl);
319     }
320 
321     protected UserIdStrategy getUserIdStrategy(
322         User user, String userIdStrategy) {
323 
324         if (UserIdStrategy.ALWAYS_CURRENT_USER_ID.equals(userIdStrategy)) {
325             return new AlwaysCurrentUserIdStrategy(user);
326         }
327 
328         return new CurrentUserIdStrategy(user);
329     }
330 
331     protected void importPortletData(
332             PortletDataContext context, String portletId, long plid,
333             Element portletDataRefEl)
334         throws PortalException, SystemException {
335 
336         try {
337             PortletPreferences portletPreferences =
338                 PortletPreferencesUtil.findByO_O_P_P(
339                     PortletKeys.PREFS_OWNER_ID_DEFAULT,
340                     PortletKeys.PREFS_OWNER_TYPE_LAYOUT, plid,
341                     portletId);
342 
343             String preferences = importPortletData(
344                 context, portletId, portletPreferences, portletDataRefEl);
345 
346             if (preferences != null) {
347                 portletPreferences.setPreferences(preferences);
348 
349                 PortletPreferencesUtil.update(portletPreferences, false);
350             }
351         }
352         catch (NoSuchPortletPreferencesException nsppe) {
353         }
354     }
355 
356     protected String importPortletData(
357             PortletDataContext context, String portletId,
358             PortletPreferences portletPreferences, Element portletDataRefEl)
359         throws PortalException, SystemException {
360 
361         Portlet portlet = PortletLocalServiceUtil.getPortletById(
362             context.getCompanyId(), portletId);
363 
364         if (portlet == null) {
365             if (_log.isDebugEnabled()) {
366                 _log.debug(
367                     "Do not import portlet data for " + portletId +
368                         " because the portlet does not exist");
369             }
370 
371             return null;
372         }
373 
374         PortletDataHandler portletDataHandler =
375             portlet.getPortletDataHandlerInstance();
376 
377         if (portletDataHandler == null) {
378             if (_log.isDebugEnabled()) {
379                 _log.debug(
380                     "Do not import portlet data for " + portletId +
381                         " because the portlet does not have a " +
382                             "PortletDataHandler");
383             }
384 
385             return null;
386         }
387 
388         if (_log.isDebugEnabled()) {
389             _log.debug("Importing data for " + portletId);
390         }
391 
392         PortletPreferencesImpl prefsImpl = null;
393 
394         if (portletPreferences != null) {
395             prefsImpl = (PortletPreferencesImpl)
396                 PortletPreferencesSerializer.fromDefaultXML(
397                     portletPreferences.getPreferences());
398         }
399 
400         String portletData = context.getZipEntryAsString(
401             portletDataRefEl.attributeValue("path"));
402 
403         prefsImpl = (PortletPreferencesImpl)portletDataHandler.importData(
404             context, portletId, prefsImpl, portletData);
405 
406         if (prefsImpl == null) {
407             return null;
408         }
409 
410         return PortletPreferencesSerializer.toXML(prefsImpl);
411     }
412 
413     protected void importPortletPreferences(
414             PortletDataContext context, long companyId, long groupId, long plid,
415             String portletId, Element parentEl, boolean importPortletSetup,
416             boolean importPortletArchivedSetups, boolean importUserPreferences)
417         throws PortalException, SystemException {
418 
419         long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
420 
421         List<Element> prefsEls = parentEl.elements("portlet-preference");
422 
423         for (Element prefEl : prefsEls) {
424             String path = prefEl.attributeValue("path");
425 
426             if (context.isPathNotProcessed(path)) {
427 
428                 Element el = null;
429                 String preferences = null;
430 
431                 try {
432                     preferences = context.getZipEntryAsString(path);
433 
434                     Document prefsDoc = DocumentUtil.readDocumentFromXML(
435                         preferences);
436 
437                     el = prefsDoc.getRootElement();
438                 }
439                 catch (DocumentException de) {
440                     throw new SystemException(de);
441                 }
442 
443                 long ownerId = GetterUtil.getLong(
444                     el.attributeValue("owner-id"));
445                 int ownerType = GetterUtil.getInteger(
446                     el.attributeValue("owner-type"));
447 
448                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_COMPANY) {
449                     continue;
450                 }
451 
452                 if (((ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) ||
453                      (ownerType == PortletKeys.PREFS_OWNER_TYPE_LAYOUT)) &&
454                     !importPortletSetup) {
455 
456                     continue;
457                 }
458 
459                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) &&
460                     !importPortletArchivedSetups) {
461 
462                     continue;
463                 }
464 
465                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_USER) &&
466                     (ownerId != PortletKeys.PREFS_OWNER_ID_DEFAULT) &&
467                     !importUserPreferences) {
468 
469                     continue;
470                 }
471 
472                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) {
473                     plid = PortletKeys.PREFS_PLID_SHARED;
474                     ownerId = context.getGroupId();
475                 }
476 
477                 boolean defaultUser = GetterUtil.getBoolean(
478                     el.attributeValue("default-user"));
479 
480                 if (portletId == null) {
481                     portletId = el.attributeValue("portlet-id");
482                 }
483 
484                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) {
485                     String userUuid = el.attributeValue("archive-user-uuid");
486                     String name = el.attributeValue("archive-name");
487 
488                     long userId = context.getUserId(userUuid);
489 
490                     PortletItem portletItem =
491                         PortletItemLocalServiceUtil.updatePortletItem(
492                             userId, groupId, name, portletId,
493                             PortletPreferences.class.getName());
494 
495                     plid = 0;
496                     ownerId = portletItem.getPortletItemId();
497                 }
498 
499                 if (defaultUser) {
500                     ownerId = defaultUserId;
501                 }
502 
503                 PortletPreferences prefs =
504                     PortletPreferencesUtil.fetchByO_O_P_P(
505                         ownerId, ownerType, plid, portletId);
506 
507                 if (prefs != null) {
508                     PortletPreferencesLocalServiceUtil.deletePortletPreferences(
509                         ownerId, ownerType, plid, portletId);
510                 }
511 
512                 long portletPreferencesId = CounterLocalServiceUtil.increment();
513 
514                 PortletPreferences portletPreferences =
515                     PortletPreferencesUtil.create(portletPreferencesId);
516 
517                 portletPreferences.setOwnerId(ownerId);
518                 portletPreferences.setOwnerType(ownerType);
519                 portletPreferences.setPlid(plid);
520                 portletPreferences.setPortletId(portletId);
521 
522                 portletPreferences.setPreferences(preferences);
523 
524                 PortletPreferencesUtil.update(portletPreferences, true);
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 = DocumentUtil.readDocumentFromXML(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, new Long(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 = DocumentUtil.readDocumentFromXML(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 = DocumentUtil.readDocumentFromXML(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 = LogFactory.getLog(PortletImporter.class);
644 
645 }