1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.journal.action;
16  
17  import com.liferay.portal.kernel.servlet.SessionErrors;
18  import com.liferay.portal.kernel.upload.UploadPortletRequest;
19  import com.liferay.portal.kernel.util.Constants;
20  import com.liferay.portal.kernel.util.FileUtil;
21  import com.liferay.portal.kernel.util.GetterUtil;
22  import com.liferay.portal.kernel.util.LocalizationUtil;
23  import com.liferay.portal.kernel.util.ParamUtil;
24  import com.liferay.portal.kernel.util.StringPool;
25  import com.liferay.portal.kernel.util.StringUtil;
26  import com.liferay.portal.kernel.util.Validator;
27  import com.liferay.portal.kernel.workflow.WorkflowConstants;
28  import com.liferay.portal.model.Layout;
29  import com.liferay.portal.security.auth.PrincipalException;
30  import com.liferay.portal.security.permission.ActionKeys;
31  import com.liferay.portal.security.permission.PermissionChecker;
32  import com.liferay.portal.service.ServiceContext;
33  import com.liferay.portal.service.ServiceContextFactory;
34  import com.liferay.portal.service.SubscriptionLocalServiceUtil;
35  import com.liferay.portal.struts.PortletAction;
36  import com.liferay.portal.theme.ThemeDisplay;
37  import com.liferay.portal.util.PortalUtil;
38  import com.liferay.portal.util.PortletKeys;
39  import com.liferay.portal.util.WebKeys;
40  import com.liferay.portlet.ActionRequestImpl;
41  import com.liferay.portlet.PortletPreferencesFactoryUtil;
42  import com.liferay.portlet.PortletURLImpl;
43  import com.liferay.portlet.asset.AssetTagException;
44  import com.liferay.portlet.assetpublisher.util.AssetPublisherUtil;
45  import com.liferay.portlet.journal.ArticleContentException;
46  import com.liferay.portlet.journal.ArticleDisplayDateException;
47  import com.liferay.portlet.journal.ArticleExpirationDateException;
48  import com.liferay.portlet.journal.ArticleIdException;
49  import com.liferay.portlet.journal.ArticleSmallImageNameException;
50  import com.liferay.portlet.journal.ArticleSmallImageSizeException;
51  import com.liferay.portlet.journal.ArticleTitleException;
52  import com.liferay.portlet.journal.ArticleTypeException;
53  import com.liferay.portlet.journal.ArticleVersionException;
54  import com.liferay.portlet.journal.DuplicateArticleIdException;
55  import com.liferay.portlet.journal.NoSuchArticleException;
56  import com.liferay.portlet.journal.NoSuchStructureException;
57  import com.liferay.portlet.journal.NoSuchTemplateException;
58  import com.liferay.portlet.journal.model.JournalArticle;
59  import com.liferay.portlet.journal.model.JournalStructure;
60  import com.liferay.portlet.journal.service.JournalArticleServiceUtil;
61  import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
62  import com.liferay.portlet.journal.service.JournalStructureLocalServiceUtil;
63  import com.liferay.portlet.journal.service.permission.JournalPermission;
64  import com.liferay.portlet.journal.util.JournalUtil;
65  
66  import java.io.File;
67  
68  import java.util.Calendar;
69  import java.util.Enumeration;
70  import java.util.HashMap;
71  import java.util.Map;
72  
73  import javax.portlet.ActionRequest;
74  import javax.portlet.ActionResponse;
75  import javax.portlet.PortletConfig;
76  import javax.portlet.PortletPreferences;
77  import javax.portlet.PortletRequest;
78  import javax.portlet.RenderRequest;
79  import javax.portlet.RenderResponse;
80  import javax.portlet.WindowState;
81  
82  import org.apache.struts.action.ActionForm;
83  import org.apache.struts.action.ActionForward;
84  import org.apache.struts.action.ActionMapping;
85  
86  /**
87   * <a href="EditArticleAction.java.html"><b><i>View Source</i></b></a>
88   *
89   * @author Brian Wing Shun Chan
90   * @author Raymond Augé
91   */
92  public class EditArticleAction extends PortletAction {
93  
94      public static final String VERSION_SEPARATOR = "_version_";
95  
96      public void processAction(
97              ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
98              ActionRequest actionRequest, ActionResponse actionResponse)
99          throws Exception {
100 
101         String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
102 
103         JournalArticle article = null;
104 
105         try {
106             if (cmd.equals(Constants.ADD) || cmd.equals(Constants.UPDATE)) {
107                 article = updateArticle(actionRequest);
108             }
109             else if (cmd.equals(Constants.DELETE)) {
110                 deleteArticles(actionRequest);
111             }
112             else if (cmd.equals(Constants.EXPIRE)) {
113                 expireArticles(actionRequest);
114             }
115             else if (cmd.equals(Constants.SUBSCRIBE)) {
116                 subscribeArticles(actionRequest);
117             }
118             else if (cmd.equals(Constants.UNSUBSCRIBE)) {
119                 unsubscribeArticles(actionRequest);
120             }
121             else if (cmd.equals("removeArticlesLocale")) {
122                 removeArticlesLocale(actionRequest);
123             }
124 
125             if (Validator.isNotNull(cmd)) {
126                 String redirect = ParamUtil.getString(
127                     actionRequest, "redirect");
128 
129                 int workflowAction = ParamUtil.getInteger(
130                     actionRequest, "workflowAction",
131                     WorkflowConstants.ACTION_PUBLISH);
132 
133                 if ((article != null) &&
134                     (workflowAction == WorkflowConstants.ACTION_SAVE_DRAFT)) {
135 
136                     redirect = getSaveAndContinueRedirect(
137                         portletConfig, actionRequest, article, redirect);
138                 }
139 
140                 String referringPortletResource = ParamUtil.getString(
141                     actionRequest, "referringPortletResource");
142 
143                 if (referringPortletResource.equals(
144                         PortletKeys.JOURNAL_CONTENT)) {
145 
146                     actionResponse.sendRedirect(redirect);
147                 }
148                 else {
149                     sendRedirect(actionRequest, actionResponse, redirect);
150                 }
151             }
152         }
153         catch (Exception e) {
154             if (e instanceof NoSuchArticleException ||
155                 e instanceof NoSuchStructureException ||
156                 e instanceof NoSuchTemplateException ||
157                 e instanceof PrincipalException) {
158 
159                 SessionErrors.add(actionRequest, e.getClass().getName());
160 
161                 setForward(actionRequest, "portlet.journal.error");
162             }
163             else if (e instanceof ArticleContentException ||
164                      e instanceof ArticleDisplayDateException ||
165                      e instanceof ArticleExpirationDateException ||
166                      e instanceof ArticleIdException ||
167                      e instanceof ArticleSmallImageNameException ||
168                      e instanceof ArticleSmallImageSizeException ||
169                      e instanceof ArticleTitleException ||
170                      e instanceof ArticleTypeException ||
171                      e instanceof ArticleVersionException ||
172                      e instanceof DuplicateArticleIdException) {
173 
174                 SessionErrors.add(actionRequest, e.getClass().getName());
175             }
176             else if (e instanceof AssetTagException) {
177                 SessionErrors.add(actionRequest, e.getClass().getName(), e);
178             }
179             else {
180                 throw e;
181             }
182         }
183     }
184 
185     public ActionForward render(
186             ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
187             RenderRequest renderRequest, RenderResponse renderResponse)
188         throws Exception {
189 
190         try {
191             String cmd = ParamUtil.getString(renderRequest, Constants.CMD);
192 
193             if (!cmd.equals(Constants.ADD)) {
194                 ActionUtil.getArticle(renderRequest);
195             }
196         }
197         catch (NoSuchArticleException nsse) {
198 
199             // Let this slide because the user can manually input a article id
200             // for a new article that does not yet exist.
201 
202         }
203         catch (Exception e) {
204             if (//e instanceof NoSuchArticleException ||
205                 e instanceof PrincipalException) {
206 
207                 SessionErrors.add(renderRequest, e.getClass().getName());
208 
209                 return mapping.findForward("portlet.journal.error");
210             }
211             else {
212                 throw e;
213             }
214         }
215 
216         return mapping.findForward(
217             getForward(renderRequest, "portlet.journal.edit_article"));
218     }
219 
220     protected void deleteArticles(ActionRequest actionRequest)
221         throws Exception {
222 
223         long groupId = ParamUtil.getLong(actionRequest, "groupId");
224 
225         String[] deleteArticleIds = StringUtil.split(
226             ParamUtil.getString(actionRequest, "deleteArticleIds"));
227 
228         ServiceContext serviceContext = ServiceContextFactory.getInstance(
229             JournalArticle.class.getName(), actionRequest);
230 
231         for (int i = 0; i < deleteArticleIds.length; i++) {
232             int pos = deleteArticleIds[i].lastIndexOf(VERSION_SEPARATOR);
233 
234             String articleId = deleteArticleIds[i];
235 
236             String articleURL = ParamUtil.getString(
237                 actionRequest, "articleURL");
238 
239             if (pos == -1) {
240                 JournalArticleServiceUtil.deleteArticle(
241                     groupId, articleId, articleURL, serviceContext);
242             }
243             else {
244                 articleId = articleId.substring(0, pos);
245                 double version = GetterUtil.getDouble(
246                     deleteArticleIds[i].substring(
247                         pos + VERSION_SEPARATOR.length()));
248 
249                 JournalArticleServiceUtil.deleteArticle(
250                     groupId, articleId, version, articleURL, serviceContext);
251             }
252 
253             JournalUtil.removeRecentArticle(actionRequest, deleteArticleIds[i]);
254         }
255     }
256 
257     protected void expireArticles(ActionRequest actionRequest)
258         throws Exception {
259 
260         long groupId = ParamUtil.getLong(actionRequest, "groupId");
261 
262         String[] expireArticleIds = StringUtil.split(
263             ParamUtil.getString(actionRequest, "expireArticleIds"));
264 
265         ServiceContext serviceContext = ServiceContextFactory.getInstance(
266             JournalArticle.class.getName(), actionRequest);
267 
268         for (int i = 0; i < expireArticleIds.length; i++) {
269             int pos = expireArticleIds[i].lastIndexOf(VERSION_SEPARATOR);
270 
271             String articleId = expireArticleIds[i].substring(0, pos);
272             double version = GetterUtil.getDouble(
273                 expireArticleIds[i].substring(
274                     pos + VERSION_SEPARATOR.length()));
275 
276             String articleURL = ParamUtil.getString(
277                 actionRequest, "articleURL");
278 
279             JournalArticleServiceUtil.expireArticle(
280                 groupId, articleId, version, articleURL, serviceContext);
281         }
282     }
283 
284     protected Map<String, byte[]> getImages(UploadPortletRequest uploadRequest)
285         throws Exception {
286 
287         Map<String, byte[]> images = new HashMap<String, byte[]>();
288 
289         String imagePrefix = "structure_image_";
290 
291         Enumeration<String> enu = uploadRequest.getParameterNames();
292 
293         while (enu.hasMoreElements()) {
294             String name = enu.nextElement();
295 
296             if (name.startsWith(imagePrefix)) {
297                 File file = uploadRequest.getFile(name);
298                 byte[] bytes = FileUtil.getBytes(file);
299 
300                 if ((bytes != null) && (bytes.length > 0)) {
301                     name = name.substring(imagePrefix.length(), name.length());
302 
303                     images.put(name, bytes);
304                 }
305             }
306         }
307 
308         return images;
309     }
310 
311     protected String getSaveAndContinueRedirect(
312             PortletConfig portletConfig, ActionRequest actionRequest,
313             JournalArticle article, String redirect)
314         throws Exception {
315 
316         ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
317             WebKeys.THEME_DISPLAY);
318 
319         String originalRedirect = ParamUtil.getString(
320             actionRequest, "originalRedirect");
321 
322         PortletURLImpl portletURL = new PortletURLImpl(
323             (ActionRequestImpl)actionRequest, portletConfig.getPortletName(),
324             themeDisplay.getPlid(), PortletRequest.RENDER_PHASE);
325 
326         portletURL.setWindowState(WindowState.MAXIMIZED);
327 
328         portletURL.setParameter("struts_action", "/journal/edit_article");
329         portletURL.setParameter(Constants.CMD, Constants.UPDATE, false);
330         portletURL.setParameter("redirect", redirect, false);
331         portletURL.setParameter("originalRedirect", originalRedirect, false);
332         portletURL.setParameter(
333             "groupId", String.valueOf(article.getGroupId()), false);
334         portletURL.setParameter("articleId", article.getArticleId(), false);
335         portletURL.setParameter(
336             "version", String.valueOf(article.getVersion()), false);
337 
338         return portletURL.toString();
339     }
340 
341     protected void removeArticlesLocale(ActionRequest actionRequest)
342         throws Exception {
343 
344         long groupId = ParamUtil.getLong(actionRequest, "groupId");
345 
346         String[] removeArticleLocaleIds = StringUtil.split(
347             ParamUtil.getString(actionRequest, "deleteArticleIds"));
348 
349         for (int i = 0; i < removeArticleLocaleIds.length; i++) {
350             int pos = removeArticleLocaleIds[i].lastIndexOf(VERSION_SEPARATOR);
351 
352             String articleId = removeArticleLocaleIds[i].substring(0, pos);
353             double version = GetterUtil.getDouble(
354                 removeArticleLocaleIds[i].substring(
355                     pos + VERSION_SEPARATOR.length()));
356             String languageId = ParamUtil.getString(
357                 actionRequest, "languageId");
358 
359             JournalArticleServiceUtil.removeArticleLocale(
360                 groupId, articleId, version, languageId);
361         }
362     }
363 
364     protected void subscribeArticles(ActionRequest actionRequest)
365         throws Exception {
366 
367         ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
368             WebKeys.THEME_DISPLAY);
369 
370         PermissionChecker permissionChecker =
371             themeDisplay.getPermissionChecker();
372 
373         if (JournalPermission.contains(
374                 permissionChecker, themeDisplay.getScopeGroupId(),
375                 ActionKeys.SUBSCRIBE)) {
376 
377             SubscriptionLocalServiceUtil.addSubscription(
378                 themeDisplay.getUserId(), JournalArticle.class.getName(),
379                 themeDisplay.getScopeGroupId());
380         }
381     }
382 
383     protected void unsubscribeArticles(ActionRequest actionRequest)
384         throws Exception {
385 
386         ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
387             WebKeys.THEME_DISPLAY);
388 
389         PermissionChecker permissionChecker =
390             themeDisplay.getPermissionChecker();
391 
392         if (JournalPermission.contains(
393                 permissionChecker, themeDisplay.getScopeGroupId(),
394                 ActionKeys.SUBSCRIBE)) {
395 
396             SubscriptionLocalServiceUtil.deleteSubscription(
397                 themeDisplay.getUserId(), JournalArticle.class.getName(),
398                 themeDisplay.getScopeGroupId());
399         }
400     }
401 
402     protected JournalArticle updateArticle(ActionRequest actionRequest)
403         throws Exception {
404 
405         UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(
406             actionRequest);
407 
408         String cmd = ParamUtil.getString(uploadRequest, Constants.CMD);
409 
410         long groupId = ParamUtil.getLong(uploadRequest, "groupId");
411 
412         String articleId = ParamUtil.getString(uploadRequest, "articleId");
413         boolean autoArticleId = ParamUtil.getBoolean(
414             uploadRequest, "autoArticleId");
415 
416         double version = ParamUtil.getDouble(uploadRequest, "version");
417 
418         String title = ParamUtil.getString(uploadRequest, "title");
419         String description = ParamUtil.getString(uploadRequest, "description");
420         String content = ParamUtil.getString(uploadRequest, "content");
421         String type = ParamUtil.getString(uploadRequest, "type");
422         String structureId = ParamUtil.getString(uploadRequest, "structureId");
423         String templateId = ParamUtil.getString(uploadRequest, "templateId");
424 
425         String lastLanguageId = ParamUtil.getString(
426             uploadRequest, "lastLanguageId");
427         String defaultLanguageId = ParamUtil.getString(
428             uploadRequest, "defaultLanguageId");
429 
430         int displayDateMonth = ParamUtil.getInteger(
431             uploadRequest, "displayDateMonth");
432         int displayDateDay = ParamUtil.getInteger(
433             uploadRequest, "displayDateDay");
434         int displayDateYear = ParamUtil.getInteger(
435             uploadRequest, "displayDateYear");
436         int displayDateHour = ParamUtil.getInteger(
437             uploadRequest, "displayDateHour");
438         int displayDateMinute = ParamUtil.getInteger(
439             uploadRequest, "displayDateMinute");
440         int displayDateAmPm = ParamUtil.getInteger(
441             uploadRequest, "displayDateAmPm");
442 
443         if (displayDateAmPm == Calendar.PM) {
444             displayDateHour += 12;
445         }
446 
447         int expirationDateMonth = ParamUtil.getInteger(
448             uploadRequest, "expirationDateMonth");
449         int expirationDateDay = ParamUtil.getInteger(
450             uploadRequest, "expirationDateDay");
451         int expirationDateYear = ParamUtil.getInteger(
452             uploadRequest, "expirationDateYear");
453         int expirationDateHour = ParamUtil.getInteger(
454             uploadRequest, "expirationDateHour");
455         int expirationDateMinute = ParamUtil.getInteger(
456             uploadRequest, "expirationDateMinute");
457         int expirationDateAmPm = ParamUtil.getInteger(
458             uploadRequest, "expirationDateAmPm");
459         boolean neverExpire = ParamUtil.getBoolean(
460             uploadRequest, "neverExpire");
461 
462         if (expirationDateAmPm == Calendar.PM) {
463             expirationDateHour += 12;
464         }
465 
466         int reviewDateMonth = ParamUtil.getInteger(
467             uploadRequest, "reviewDateMonth");
468         int reviewDateDay = ParamUtil.getInteger(
469             uploadRequest, "reviewDateDay");
470         int reviewDateYear = ParamUtil.getInteger(
471             uploadRequest, "reviewDateYear");
472         int reviewDateHour = ParamUtil.getInteger(
473             uploadRequest, "reviewDateHour");
474         int reviewDateMinute = ParamUtil.getInteger(
475             uploadRequest, "reviewDateMinute");
476         int reviewDateAmPm = ParamUtil.getInteger(
477             uploadRequest, "reviewDateAmPm");
478         boolean neverReview = ParamUtil.getBoolean(
479             uploadRequest, "neverReview");
480 
481         if (reviewDateAmPm == Calendar.PM) {
482             reviewDateHour += 12;
483         }
484 
485         boolean indexable = ParamUtil.getBoolean(uploadRequest, "indexable");
486 
487         boolean smallImage = ParamUtil.getBoolean(uploadRequest, "smallImage");
488         String smallImageURL = ParamUtil.getString(
489             uploadRequest, "smallImageURL");
490         File smallFile = uploadRequest.getFile("smallFile");
491 
492         Map<String, byte[]> images = getImages(uploadRequest);
493 
494         String articleURL = ParamUtil.getString(uploadRequest, "articleURL");
495 
496         ServiceContext serviceContext = ServiceContextFactory.getInstance(
497             JournalArticle.class.getName(), actionRequest);
498 
499         JournalArticle article = null;
500 
501         if (cmd.equals(Constants.ADD)) {
502             if (Validator.isNull(structureId)) {
503                 content = LocalizationUtil.updateLocalization(
504                     StringPool.BLANK, "static-content", content,
505                     lastLanguageId, defaultLanguageId, true);
506             }
507 
508             // Add article
509 
510             article = JournalArticleServiceUtil.addArticle(
511                 groupId, articleId, autoArticleId, title, description,
512                 content, type, structureId, templateId, displayDateMonth,
513                 displayDateDay, displayDateYear, displayDateHour,
514                 displayDateMinute, expirationDateMonth, expirationDateDay,
515                 expirationDateYear, expirationDateHour, expirationDateMinute,
516                 neverExpire, reviewDateMonth, reviewDateDay, reviewDateYear,
517                 reviewDateHour, reviewDateMinute, neverReview, indexable,
518                 smallImage, smallImageURL, smallFile, images, articleURL,
519                 serviceContext);
520 
521             AssetPublisherUtil.addAndStoreSelection(
522                 actionRequest, JournalArticle.class.getName(),
523                 article.getResourcePrimKey(), -1);
524         }
525         else {
526 
527             // Merge current content with new content
528 
529             JournalArticle curArticle = JournalArticleServiceUtil.getArticle(
530                 groupId, articleId, version);
531 
532             if (Validator.isNull(structureId)) {
533                 if (!curArticle.isTemplateDriven()) {
534                     content = LocalizationUtil.updateLocalization(
535                         curArticle.getContent(), "static-content", content,
536                         lastLanguageId, defaultLanguageId, true);
537                 }
538             }
539             else {
540                 if (curArticle.isTemplateDriven()) {
541                     JournalStructure structure =
542                         JournalStructureLocalServiceUtil.getStructure(
543                             groupId, structureId);
544 
545                     content = JournalUtil.mergeArticleContent(
546                         curArticle.getContent(), content);
547                     content = JournalUtil.removeOldContent(
548                         content, structure.getMergedXsd());
549                 }
550             }
551 
552             // Update article
553 
554             article = JournalArticleServiceUtil.updateArticle(
555                 groupId, articleId, version, title, description, content, type,
556                 structureId, templateId, displayDateMonth, displayDateDay,
557                 displayDateYear, displayDateHour, displayDateMinute,
558                 expirationDateMonth, expirationDateDay, expirationDateYear,
559                 expirationDateHour, expirationDateMinute, neverExpire,
560                 reviewDateMonth, reviewDateDay, reviewDateYear, reviewDateHour,
561                 reviewDateMinute, neverReview, indexable, smallImage,
562                 smallImageURL, smallFile, images, articleURL, serviceContext);
563         }
564 
565         // Recent articles
566 
567         JournalUtil.addRecentArticle(actionRequest, article);
568 
569         // Journal content
570 
571         String portletResource = ParamUtil.getString(
572             uploadRequest, "portletResource");
573 
574         if (Validator.isNotNull(portletResource)) {
575             PortletPreferences preferences =
576                 PortletPreferencesFactoryUtil.getPortletSetup(
577                     uploadRequest, portletResource);
578 
579             preferences.setValue(
580                 "group-id", String.valueOf(article.getGroupId()));
581             preferences.setValue("article-id", article.getArticleId());
582 
583             preferences.store();
584 
585             updateContentSearch(
586                 actionRequest, portletResource, article.getArticleId());
587         }
588 
589         return article;
590     }
591 
592     protected void updateContentSearch(
593             ActionRequest actionRequest, String portletResource,
594             String articleId)
595         throws Exception {
596 
597         ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
598             WebKeys.THEME_DISPLAY);
599 
600         Layout layout = themeDisplay.getLayout();
601 
602         JournalContentSearchLocalServiceUtil.updateContentSearch(
603             layout.getGroupId(), layout.isPrivateLayout(), layout.getLayoutId(),
604             portletResource, articleId);
605     }
606 
607 }