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