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.NoSuchPortletPreferencesException;
26  import com.liferay.portal.PortalException;
27  import com.liferay.portal.PortletIdException;
28  import com.liferay.portal.SystemException;
29  import com.liferay.portal.kernel.log.Log;
30  import com.liferay.portal.kernel.log.LogFactoryUtil;
31  import com.liferay.portal.kernel.util.GetterUtil;
32  import com.liferay.portal.kernel.util.MapUtil;
33  import com.liferay.portal.kernel.util.ObjectValuePair;
34  import com.liferay.portal.kernel.util.PortletClassInvoker;
35  import com.liferay.portal.kernel.util.ReleaseInfo;
36  import com.liferay.portal.kernel.util.StringUtil;
37  import com.liferay.portal.kernel.util.Validator;
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  
62  import java.io.InputStream;
63  
64  import java.util.ArrayList;
65  import java.util.HashSet;
66  import java.util.List;
67  import java.util.Map;
68  
69  import org.apache.commons.lang.time.StopWatch;
70  
71  /**
72   * <a href="PortletImporter.java.html"><b><i>View Source</i></b></a>
73   *
74   * @author Brian Wing Shun Chan
75   * @author Joel Kozikowski
76   * @author Charles May
77   * @author Raymond Augé
78   * @author Jorge Ferrer
79   * @author Bruno Farache
80   *
81   */
82  public class PortletImporter {
83  
84      public void importPortletInfo(
85              long userId, long plid, 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, layout.getGroupId(), parameterMap, new HashSet<String>(),
122             strategy, 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(), layout.getGroupId(), plid,
220             portletId, portletEl, importPortletSetup,
221             importPortletArchivedSetups, 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         try {
254             long ownerId = PortletKeys.PREFS_OWNER_ID_DEFAULT;
255             int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
256 
257             PortletPreferences portletPreferences =
258                 PortletPreferencesUtil.findByO_O_P_P(
259                     ownerId, ownerType, plid, portletId);
260 
261             String xml = deletePortletData(
262                 context, portletId, portletPreferences);
263 
264             if (xml != null) {
265                 PortletPreferencesLocalServiceUtil.updatePreferences(
266                     ownerId, ownerType, plid, portletId, xml);
267             }
268         }
269         catch (NoSuchPortletPreferencesException nsppe) {
270         }
271     }
272 
273     protected String deletePortletData(
274             PortletDataContext context, String portletId,
275             PortletPreferences portletPreferences)
276         throws SystemException {
277 
278         Portlet portlet = PortletLocalServiceUtil.getPortletById(
279             context.getCompanyId(), portletId);
280 
281         if (portlet == null) {
282             if (_log.isDebugEnabled()) {
283                 _log.debug(
284                     "Do not delete portlet data for " + portletId +
285                         " because the portlet does not exist");
286             }
287 
288             return null;
289         }
290 
291         String portletDataHandlerClass =
292             portlet.getPortletDataHandlerClass();
293 
294         if (Validator.isNull(portletDataHandlerClass)) {
295             if (_log.isDebugEnabled()) {
296                 _log.debug(
297                     "Do not delete portlet data for " + portletId +
298                         " because the portlet does not have a " +
299                             "PortletDataHandler");
300             }
301 
302             return null;
303         }
304 
305         if (_log.isDebugEnabled()) {
306             _log.debug("Deleting data for " + portletId);
307         }
308 
309         PortletPreferencesImpl prefsImpl =
310             (PortletPreferencesImpl)PortletPreferencesSerializer.fromDefaultXML(
311                 portletPreferences.getPreferences());
312 
313         try {
314             prefsImpl = (PortletPreferencesImpl)PortletClassInvoker.invoke(
315                 portletId, portletDataHandlerClass, "deleteData", context,
316                 portletId, prefsImpl);
317         }
318         catch (Exception e) {
319             throw new SystemException(e);
320         }
321 
322         if (prefsImpl == null) {
323             return null;
324         }
325 
326         return PortletPreferencesSerializer.toXML(prefsImpl);
327     }
328 
329     protected UserIdStrategy getUserIdStrategy(
330         User user, String userIdStrategy) {
331 
332         if (UserIdStrategy.ALWAYS_CURRENT_USER_ID.equals(userIdStrategy)) {
333             return new AlwaysCurrentUserIdStrategy(user);
334         }
335 
336         return new CurrentUserIdStrategy(user);
337     }
338 
339     protected void importPortletData(
340             PortletDataContext context, String portletId, long plid,
341             Element portletDataRefEl)
342         throws SystemException {
343 
344         try {
345             long ownerId = PortletKeys.PREFS_OWNER_ID_DEFAULT;
346             int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
347 
348             PortletPreferences portletPreferences =
349                 PortletPreferencesUtil.findByO_O_P_P(
350                     ownerId, ownerType, plid, portletId);
351 
352             String xml = importPortletData(
353                 context, portletId, portletPreferences, portletDataRefEl);
354 
355             if (xml != null) {
356                 PortletPreferencesLocalServiceUtil.updatePreferences(
357                     ownerId, ownerType, plid, portletId, xml);
358             }
359         }
360         catch (NoSuchPortletPreferencesException nsppe) {
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         String portletDataHandlerClass =
383             portlet.getPortletDataHandlerClass();
384 
385         if (Validator.isNull(portletDataHandlerClass)) {
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             prefsImpl = (PortletPreferencesImpl)PortletClassInvoker.invoke(
413                 portletId, portletDataHandlerClass, "importData", context,
414                 portletId, prefsImpl, portletData);
415         }
416         catch (Exception e) {
417             throw new SystemException(e);
418         }
419 
420         if (prefsImpl == null) {
421             return null;
422         }
423 
424         return PortletPreferencesSerializer.toXML(prefsImpl);
425     }
426 
427     protected void importPortletPreferences(
428             PortletDataContext context, long companyId, long groupId, long plid,
429             String portletId, Element parentEl, boolean importPortletSetup,
430             boolean importPortletArchivedSetups, boolean importUserPreferences)
431         throws PortalException, SystemException {
432 
433         long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
434 
435         List<Element> prefsEls = parentEl.elements("portlet-preferences");
436 
437         for (Element prefEl : prefsEls) {
438             String path = prefEl.attributeValue("path");
439 
440             if (context.isPathNotProcessed(path)) {
441                 Element el = null;
442                 String xml = null;
443 
444                 try {
445                     xml = context.getZipEntryAsString(path);
446 
447                     Document prefsDoc = SAXReaderUtil.read(xml);
448 
449                     el = prefsDoc.getRootElement();
450                 }
451                 catch (DocumentException de) {
452                     throw new SystemException(de);
453                 }
454 
455                 long ownerId = GetterUtil.getLong(
456                     el.attributeValue("owner-id"));
457                 int ownerType = GetterUtil.getInteger(
458                     el.attributeValue("owner-type"));
459 
460                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_COMPANY) {
461                     continue;
462                 }
463 
464                 if (((ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) ||
465                      (ownerType == PortletKeys.PREFS_OWNER_TYPE_LAYOUT)) &&
466                     !importPortletSetup) {
467 
468                     continue;
469                 }
470 
471                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) &&
472                     !importPortletArchivedSetups) {
473 
474                     continue;
475                 }
476 
477                 if ((ownerType == PortletKeys.PREFS_OWNER_TYPE_USER) &&
478                     (ownerId != PortletKeys.PREFS_OWNER_ID_DEFAULT) &&
479                     !importUserPreferences) {
480 
481                     continue;
482                 }
483 
484                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_GROUP) {
485                     plid = PortletKeys.PREFS_PLID_SHARED;
486                     ownerId = context.getGroupId();
487                 }
488 
489                 boolean defaultUser = GetterUtil.getBoolean(
490                     el.attributeValue("default-user"));
491 
492                 if (portletId == null) {
493                     portletId = el.attributeValue("portlet-id");
494                 }
495 
496                 if (ownerType == PortletKeys.PREFS_OWNER_TYPE_ARCHIVED) {
497                     String userUuid = el.attributeValue("archive-user-uuid");
498                     String name = el.attributeValue("archive-name");
499 
500                     long userId = context.getUserId(userUuid);
501 
502                     PortletItem portletItem =
503                         PortletItemLocalServiceUtil.updatePortletItem(
504                             userId, groupId, name, portletId,
505                             PortletPreferences.class.getName());
506 
507                     plid = 0;
508                     ownerId = portletItem.getPortletItemId();
509                 }
510 
511                 if (defaultUser) {
512                     ownerId = defaultUserId;
513                 }
514 
515                 PortletPreferencesLocalServiceUtil.updatePreferences(
516                     ownerId, ownerType, plid, portletId, xml);
517             }
518         }
519     }
520 
521     protected void readComments(PortletDataContext context, Element parentEl)
522         throws SystemException {
523 
524         try {
525             String xml = context.getZipEntryAsString(
526                 context.getImportRootPath() + "/comments.xml");
527 
528             Document doc = SAXReaderUtil.read(xml);
529 
530             Element root = doc.getRootElement();
531 
532             List<Element> assets = root.elements("asset");
533 
534             for (Element asset : assets) {
535                 String path = asset.attributeValue("path");
536                 String className = asset.attributeValue("class-name");
537                 long classPK = GetterUtil.getLong(
538                     asset.attributeValue("class-pk"));
539 
540                 List<ObjectValuePair<String, byte[]>> entries =
541                     context.getZipFolderEntries(path);
542 
543                 List<MBMessage> messages = new ArrayList<MBMessage>();
544 
545                 for (ObjectValuePair<String, byte[]> entry : entries) {
546                     if (entry.getValue().length > 0) {
547                         MBMessage message = (MBMessage)context.fromXML(
548                             entry.getValue());
549 
550                         messages.add(message);
551                     }
552                 }
553 
554                 context.addComments(className, classPK, messages);
555             }
556         }
557         catch (Exception e) {
558             throw new SystemException(e);
559         }
560     }
561 
562     protected void readRatings(PortletDataContext context, Element parentEl)
563         throws SystemException {
564 
565         try {
566             String xml = context.getZipEntryAsString(
567                 context.getImportRootPath() + "/ratings.xml");
568 
569             Document doc = SAXReaderUtil.read(xml);
570 
571             Element root = doc.getRootElement();
572 
573             List<Element> assets = root.elements("asset");
574 
575             for (Element asset : assets) {
576                 String path = asset.attributeValue("path");
577                 String className = asset.attributeValue("class-name");
578                 long classPK = GetterUtil.getLong(
579                     asset.attributeValue("class-pk"));
580 
581                 List<ObjectValuePair<String, byte[]>> entries =
582                     context.getZipFolderEntries(path);
583 
584                 List<RatingsEntry> ratings = new ArrayList<RatingsEntry>();
585 
586                 for (ObjectValuePair<String, byte[]> entry : entries) {
587                     if (entry.getValue().length > 0) {
588                         RatingsEntry rating = (RatingsEntry)context.fromXML(
589                             entry.getValue());
590 
591                         ratings.add(rating);
592                     }
593                 }
594 
595                 context.addRatingsEntries(
596                     className, new Long(classPK), ratings);
597             }
598         }
599         catch (Exception e) {
600             throw new SystemException(e);
601         }
602     }
603 
604     protected void readTags(PortletDataContext context, Element parentEl)
605         throws SystemException {
606 
607         try {
608             String xml = context.getZipEntryAsString(
609                 context.getImportRootPath() + "/tags.xml");
610 
611             Document doc = SAXReaderUtil.read(xml);
612 
613             Element root = doc.getRootElement();
614 
615             List<Element> assets = root.elements("asset");
616 
617             for (Element asset : assets) {
618                 String className = GetterUtil.getString(
619                     asset.attributeValue("class-name"));
620                 long classPK = GetterUtil.getLong(
621                     asset.attributeValue("class-pk"));
622                 String entries = GetterUtil.getString(
623                     asset.attributeValue("entries"));
624 
625                 context.addTagsEntries(
626                     className, new Long(classPK),
627                     StringUtil.split(entries, ","));
628             }
629         }
630         catch (Exception e) {
631             throw new SystemException(e);
632         }
633     }
634 
635     private static Log _log = LogFactoryUtil.getLog(PortletImporter.class);
636 
637 }