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