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(el.attributeValue("owner-id"));
444                 int ownerType = GetterUtil.getInteger(
445                     el.attributeValue("owner-type"));
446 
447                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_COMPANY) {
448                     continue;
449                 }
450 
451                 if (((ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) ||
452                      (ownerType == PortletKeys.PREFS_OWNER_TYPE_LAYOUT)) &&
453                     !importPortletSetup) {
454 
455                     continue;
456                 }
457 
458                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) &&
459                     !importPortletArchivedSetups) {
460 
461                     continue;
462                 }
463 
464                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_USER) &&
465                     (ownerId != PortletKeys.PREFS_OWNER_ID_DEFAULT) &&
466                     !importUserPreferences) {
467 
468                     continue;
469                 }
470 
471                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) {
472                     plid = PortletKeys.PREFS_PLID_SHARED;
473                     ownerId = context.getGroupId();
474                 }
475 
476                 boolean defaultUser = GetterUtil.getBoolean(
477                     el.attributeValue("default-user"));
478 
479                 if (portletId == null) {
480                     portletId = el.attributeValue("portlet-id");
481                 }
482 
483                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) {
484                     String userUuid = el.attributeValue("archive-user-uuid");
485                     String name = el.attributeValue("archive-name");
486 
487                     long userId = context.getUserId(userUuid);
488 
489                     PortletItem portletItem =
490                         PortletItemLocalServiceUtil.updatePortletItem(
491                             userId, groupId, name, portletId,
492                             PortletPreferences.class.getName());
493 
494                     plid = 0;
495                     ownerId = portletItem.getPortletItemId();
496                 }
497 
498                 if (defaultUser) {
499                     ownerId = defaultUserId;
500                 }
501 
502                 PortletPreferences prefs =
503                     PortletPreferencesUtil.fetchByO_O_P_P(
504                         ownerId, ownerType, plid, portletId);
505 
506                 if (prefs != null) {
507                     PortletPreferencesLocalServiceUtil.deletePortletPreferences(
508                         ownerId, ownerType, plid, portletId);
509                 }
510 
511                 long portletPreferencesId = CounterLocalServiceUtil.increment();
512 
513                 PortletPreferences portletPreferences =
514                     PortletPreferencesUtil.create(portletPreferencesId);
515 
516                 portletPreferences.setOwnerId(ownerId);
517                 portletPreferences.setOwnerType(ownerType);
518                 portletPreferences.setPlid(plid);
519                 portletPreferences.setPortletId(portletId);
520 
521                 portletPreferences.setPreferences(preferences);
522 
523                 PortletPreferencesUtil.update(portletPreferences, true);
524             }
525         }
526     }
527 
528     protected void readComments(PortletDataContext context, Element parentEl)
529         throws SystemException {
530 
531         try {
532             String xml = context.getZipEntryAsString(
533                 context.getImportRootPath() + "/comments.xml");
534 
535             Document doc = DocumentUtil.readDocumentFromXML(xml);
536 
537             Element root = doc.getRootElement();
538 
539             List<Element> assets = root.elements("asset");
540 
541             for (Element asset : assets) {
542                 String path = asset.attributeValue("path");
543                 String className = asset.attributeValue("class-name");
544                 long classPK = GetterUtil.getLong(
545                     asset.attributeValue("class-pk"));
546 
547                 List<ObjectValuePair<String, byte[]>> entries =
548                     context.getZipFolderEntries(path);
549 
550                 List<MBMessage> messages = new ArrayList<MBMessage>();
551 
552                 for (ObjectValuePair<String, byte[]> entry : entries) {
553                     MBMessage message =
554                         (MBMessage)context.fromXML(entry.getValue());
555 
556                     messages.add(message);
557                 }
558 
559                 context.addComments(className, new Long(classPK), messages);
560             }
561         }
562         catch (Exception e) {
563             throw new SystemException(e);
564         }
565     }
566 
567     protected void readRatings(PortletDataContext context, Element parentEl)
568         throws SystemException {
569 
570         try {
571             String xml = context.getZipEntryAsString(
572                 context.getImportRootPath() + "/ratings.xml");
573 
574             Document doc = DocumentUtil.readDocumentFromXML(xml);
575 
576             Element root = doc.getRootElement();
577 
578             List<Element> assets = root.elements("asset");
579 
580             for (Element asset : assets) {
581                 String path = asset.attributeValue("path");
582                 String className = asset.attributeValue("class-name");
583                 long classPK = GetterUtil.getLong(
584                     asset.attributeValue("class-pk"));
585 
586                 List<ObjectValuePair<String, byte[]>> entries =
587                     context.getZipFolderEntries(path);
588 
589                 List<RatingsEntry> ratings = new ArrayList<RatingsEntry>();
590 
591                 for (ObjectValuePair<String, byte[]> entry : entries) {
592                     RatingsEntry rating =
593                         (RatingsEntry)context.fromXML(entry.getValue());
594 
595                     ratings.add(rating);
596                 }
597 
598                 context.addRatingsEntries(className, new Long(classPK), ratings);
599             }
600         }
601         catch (Exception e) {
602             throw new SystemException(e);
603         }
604     }
605 
606     protected void readTags(PortletDataContext context, Element parentEl)
607         throws SystemException {
608 
609         try {
610             String xml = context.getZipEntryAsString(
611                 context.getImportRootPath() + "/tags.xml");
612 
613             Document doc = DocumentUtil.readDocumentFromXML(xml);
614 
615             Element root = doc.getRootElement();
616 
617             List<Element> assets = root.elements("asset");
618 
619             for (Element asset : assets) {
620                 String className = GetterUtil.getString(
621                     asset.attributeValue("class-name"));
622                 long classPK = GetterUtil.getLong(
623                     asset.attributeValue("class-pk"));
624                 String entries = GetterUtil.getString(
625                     asset.attributeValue("entries"));
626 
627                 context.addTagsEntries(
628                     className, new Long(classPK),
629                     StringUtil.split(entries, ","));
630             }
631         }
632         catch (Exception e) {
633             throw new SystemException(e);
634         }
635     }
636 
637     private static Log _log = LogFactory.getLog(PortletImporter.class);
638 
639 }