1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.journal.action;
24  
25  import com.liferay.portal.kernel.util.Constants;
26  import com.liferay.portal.kernel.util.GetterUtil;
27  import com.liferay.portal.kernel.util.ParamUtil;
28  import com.liferay.portal.kernel.util.StringPool;
29  import com.liferay.portal.kernel.util.StringUtil;
30  import com.liferay.portal.kernel.util.Validator;
31  import com.liferay.portal.model.Layout;
32  import com.liferay.portal.security.auth.PrincipalException;
33  import com.liferay.portal.struts.PortletAction;
34  import com.liferay.portal.theme.ThemeDisplay;
35  import com.liferay.portal.util.PortalUtil;
36  import com.liferay.portal.util.WebKeys;
37  import com.liferay.portlet.ActionRequestImpl;
38  import com.liferay.portlet.PortletPreferencesFactoryUtil;
39  import com.liferay.portlet.PortletURLImpl;
40  import com.liferay.portlet.journal.ArticleContentException;
41  import com.liferay.portlet.journal.ArticleDisplayDateException;
42  import com.liferay.portlet.journal.ArticleExpirationDateException;
43  import com.liferay.portlet.journal.ArticleIdException;
44  import com.liferay.portlet.journal.ArticleSmallImageNameException;
45  import com.liferay.portlet.journal.ArticleSmallImageSizeException;
46  import com.liferay.portlet.journal.ArticleTitleException;
47  import com.liferay.portlet.journal.ArticleTypeException;
48  import com.liferay.portlet.journal.DuplicateArticleIdException;
49  import com.liferay.portlet.journal.NoSuchArticleException;
50  import com.liferay.portlet.journal.NoSuchStructureException;
51  import com.liferay.portlet.journal.NoSuchTemplateException;
52  import com.liferay.portlet.journal.model.JournalArticle;
53  import com.liferay.portlet.journal.model.JournalStructure;
54  import com.liferay.portlet.journal.service.JournalArticleServiceUtil;
55  import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
56  import com.liferay.portlet.journal.service.JournalStructureLocalServiceUtil;
57  import com.liferay.portlet.journal.util.JournalUtil;
58  import com.liferay.portlet.taggedcontent.util.AssetPublisherUtil;
59  import com.liferay.portlet.tags.TagsEntryException;
60  import com.liferay.util.FileUtil;
61  import com.liferay.util.LocalizationUtil;
62  import com.liferay.util.servlet.SessionErrors;
63  import com.liferay.util.servlet.UploadPortletRequest;
64  
65  import java.io.File;
66  
67  import java.util.Calendar;
68  import java.util.Enumeration;
69  import java.util.HashMap;
70  import java.util.Map;
71  
72  import javax.portlet.ActionRequest;
73  import javax.portlet.ActionResponse;
74  import javax.portlet.PortletConfig;
75  import javax.portlet.PortletPreferences;
76  import javax.portlet.RenderRequest;
77  import javax.portlet.RenderResponse;
78  import javax.portlet.WindowState;
79  
80  import org.apache.struts.action.ActionForm;
81  import org.apache.struts.action.ActionForward;
82  import org.apache.struts.action.ActionMapping;
83  
84  /**
85   * <a href="EditArticleAction.java.html"><b><i>View Source</i></b></a>
86   *
87   * @author Brian Wing Shun Chan
88   *
89   */
90  public class EditArticleAction extends PortletAction {
91  
92      public static final String VERSION_SEPARATOR = "_version_";
93  
94      public void processAction(
95              ActionMapping mapping, ActionForm form, PortletConfig config,
96              ActionRequest req, ActionResponse res)
97          throws Exception {
98  
99          String cmd = ParamUtil.getString(req, Constants.CMD);
100 
101         JournalArticle article = null;
102 
103         try {
104             if (cmd.equals(Constants.ADD) || cmd.equals(Constants.UPDATE)) {
105                 article = updateArticle(req);
106             }
107             else if (cmd.equals(Constants.APPROVE)) {
108                 approveArticle(req);
109             }
110             else if (cmd.equals(Constants.DELETE)) {
111                 deleteArticles(req);
112             }
113             else if (cmd.equals(Constants.EXPIRE)) {
114                 expireArticles(req);
115             }
116             else if (cmd.equals("removeArticlesLocale")) {
117                 removeArticlesLocale(req);
118             }
119 
120             if (Validator.isNotNull(cmd)) {
121                 String redirect = ParamUtil.getString(req, "redirect");
122 
123                 if (article != null) {
124                     boolean saveAndContinue = ParamUtil.getBoolean(
125                         req, "saveAndContinue");
126 
127                     if (saveAndContinue) {
128                         redirect = getSaveAndContinueRedirect(
129                             config, req, article, redirect);
130                     }
131                 }
132 
133                 sendRedirect(req, res, redirect);
134             }
135         }
136         catch (Exception e) {
137             if (e instanceof NoSuchArticleException ||
138                 e instanceof NoSuchStructureException ||
139                 e instanceof NoSuchTemplateException ||
140                 e instanceof PrincipalException) {
141 
142                 SessionErrors.add(req, e.getClass().getName());
143 
144                 setForward(req, "portlet.journal.error");
145             }
146             else if (e instanceof ArticleContentException ||
147                      e instanceof ArticleDisplayDateException ||
148                      e instanceof ArticleExpirationDateException ||
149                      e instanceof ArticleIdException ||
150                      e instanceof ArticleSmallImageNameException ||
151                      e instanceof ArticleSmallImageSizeException ||
152                      e instanceof ArticleTitleException ||
153                      e instanceof ArticleTypeException ||
154                      e instanceof DuplicateArticleIdException) {
155 
156                 SessionErrors.add(req, e.getClass().getName());
157             }
158             else if (e instanceof TagsEntryException) {
159                 SessionErrors.add(req, e.getClass().getName(), e);
160             }
161             else {
162                 throw e;
163             }
164         }
165     }
166 
167     public ActionForward render(
168             ActionMapping mapping, ActionForm form, PortletConfig config,
169             RenderRequest req, RenderResponse res)
170         throws Exception {
171 
172         try {
173             String cmd = ParamUtil.getString(req, Constants.CMD);
174 
175             if (!cmd.equals(Constants.ADD)) {
176                 ActionUtil.getArticle(req);
177             }
178         }
179         catch (NoSuchArticleException nsse) {
180 
181             // Let this slide because the user can manually input a article id
182             // for a new article that does not yet exist.
183 
184         }
185         catch (Exception e) {
186             if (//e instanceof NoSuchArticleException ||
187                 e instanceof PrincipalException) {
188 
189                 SessionErrors.add(req, e.getClass().getName());
190 
191                 return mapping.findForward("portlet.journal.error");
192             }
193             else {
194                 throw e;
195             }
196         }
197 
198         return mapping.findForward(
199             getForward(req, "portlet.journal.edit_article"));
200     }
201 
202     protected void approveArticle(ActionRequest req) throws Exception {
203         Layout layout = (Layout)req.getAttribute(WebKeys.LAYOUT);
204 
205         long groupId = ParamUtil.getLong(req, "groupId");
206         String articleId = ParamUtil.getString(req, "articleId");
207         double version = ParamUtil.getDouble(req, "version");
208 
209         String articleURL = ParamUtil.getString(req, "articleURL");
210 
211         JournalArticleServiceUtil.approveArticle(
212             groupId, articleId, version, layout.getPlid(), articleURL,
213             req.getPreferences());
214     }
215 
216     protected void deleteArticles(ActionRequest req) throws Exception {
217         long groupId = ParamUtil.getLong(req, "groupId");
218 
219         String[] deleteArticleIds = StringUtil.split(
220             ParamUtil.getString(req, "deleteArticleIds"));
221 
222         for (int i = 0; i < deleteArticleIds.length; i++) {
223             int pos = deleteArticleIds[i].lastIndexOf(VERSION_SEPARATOR);
224 
225             String articleId = deleteArticleIds[i].substring(0, pos);
226             double version = GetterUtil.getDouble(
227                 deleteArticleIds[i].substring(
228                     pos + VERSION_SEPARATOR.length()));
229 
230             JournalArticleServiceUtil.deleteArticle(
231                 groupId, articleId, version, null, null);
232 
233             JournalUtil.removeRecentArticle(req, deleteArticleIds[i]);
234         }
235     }
236 
237     protected void expireArticles(ActionRequest req) throws Exception {
238         long groupId = ParamUtil.getLong(req, "groupId");
239 
240         String[] expireArticleIds = StringUtil.split(
241             ParamUtil.getString(req, "expireArticleIds"));
242 
243         for (int i = 0; i < expireArticleIds.length; i++) {
244             int pos = expireArticleIds[i].lastIndexOf(VERSION_SEPARATOR);
245 
246             String articleId = expireArticleIds[i].substring(0, pos);
247             double version = GetterUtil.getDouble(
248                 expireArticleIds[i].substring(
249                     pos + VERSION_SEPARATOR.length()));
250 
251             JournalArticleServiceUtil.expireArticle(
252                 groupId, articleId, version, null, null);
253         }
254     }
255 
256     protected Map getImages(UploadPortletRequest uploadReq) throws Exception {
257         Map images = new HashMap();
258 
259         String imagePrefix = "structure_image_";
260 
261         Enumeration enu = uploadReq.getParameterNames();
262 
263         while (enu.hasMoreElements()) {
264             String name = (String)enu.nextElement();
265 
266             if (name.startsWith(imagePrefix)) {
267                 File file = uploadReq.getFile(name);
268                 byte[] bytes = FileUtil.getBytes(file);
269 
270                 if ((bytes != null) && (bytes.length > 0)) {
271                     name = name.substring(imagePrefix.length(), name.length());
272 
273                     images.put(name, bytes);
274                 }
275             }
276         }
277 
278         return images;
279     }
280 
281     protected String getSaveAndContinueRedirect(
282             PortletConfig config, ActionRequest req, JournalArticle article,
283             String redirect)
284         throws Exception {
285 
286         ThemeDisplay themeDisplay = (ThemeDisplay)req.getAttribute(
287             WebKeys.THEME_DISPLAY);
288 
289         String originalRedirect = ParamUtil.getString(req, "originalRedirect");
290 
291         PortletURLImpl portletURL = new PortletURLImpl(
292             (ActionRequestImpl)req, config.getPortletName(),
293             themeDisplay.getPlid(), false);
294 
295         portletURL.setWindowState(WindowState.MAXIMIZED);
296 
297         portletURL.setParameter("struts_action", "/journal/edit_article");
298         portletURL.setParameter(Constants.CMD, Constants.UPDATE, false);
299         portletURL.setParameter("redirect", redirect, false);
300         portletURL.setParameter("originalRedirect", originalRedirect, false);
301         portletURL.setParameter(
302             "groupId", String.valueOf(article.getGroupId()), false);
303         portletURL.setParameter("articleId", article.getArticleId(), false);
304         portletURL.setParameter(
305             "version", String.valueOf(article.getVersion()), false);
306 
307         return portletURL.toString();
308     }
309 
310     protected void removeArticlesLocale(ActionRequest req) throws Exception {
311         long groupId = ParamUtil.getLong(req, "groupId");
312 
313         String[] removeArticleLocaleIds = StringUtil.split(
314             ParamUtil.getString(req, "deleteArticleIds"));
315 
316         for (int i = 0; i < removeArticleLocaleIds.length; i++) {
317             int pos = removeArticleLocaleIds[i].lastIndexOf(VERSION_SEPARATOR);
318 
319             String articleId = removeArticleLocaleIds[i].substring(0, pos);
320             double version = GetterUtil.getDouble(
321                 removeArticleLocaleIds[i].substring(
322                     pos + VERSION_SEPARATOR.length()));
323             String languageId = ParamUtil.getString(req, "languageId");
324 
325             JournalArticleServiceUtil.removeArticleLocale(
326                 groupId, articleId, version, languageId);
327         }
328     }
329 
330     protected JournalArticle updateArticle(ActionRequest req) throws Exception {
331         UploadPortletRequest uploadReq =
332             PortalUtil.getUploadPortletRequest(req);
333 
334         String cmd = ParamUtil.getString(uploadReq, Constants.CMD);
335 
336         Layout layout = (Layout)uploadReq.getAttribute(WebKeys.LAYOUT);
337 
338         long groupId = ParamUtil.getLong(uploadReq, "groupId");
339 
340         String articleId = ParamUtil.getString(uploadReq, "articleId");
341         boolean autoArticleId = ParamUtil.getBoolean(
342             uploadReq, "autoArticleId");
343 
344         double version = ParamUtil.getDouble(uploadReq, "version");
345         boolean incrementVersion = ParamUtil.getBoolean(
346             uploadReq, "incrementVersion");
347 
348         String title = ParamUtil.getString(uploadReq, "title");
349         String description = ParamUtil.getString(uploadReq, "description");
350         String content = ParamUtil.getString(uploadReq, "content");
351         String type = ParamUtil.getString(uploadReq, "type");
352         String structureId = ParamUtil.getString(uploadReq, "structureId");
353         String templateId = ParamUtil.getString(uploadReq, "templateId");
354 
355         String lastLanguageId = ParamUtil.getString(
356             uploadReq, "lastLanguageId");
357         String defaultLanguageId = ParamUtil.getString(
358             uploadReq, "defaultLanguageId");
359 
360         int displayDateMonth = ParamUtil.getInteger(
361             uploadReq, "displayDateMonth");
362         int displayDateDay = ParamUtil.getInteger(uploadReq, "displayDateDay");
363         int displayDateYear = ParamUtil.getInteger(
364             uploadReq, "displayDateYear");
365         int displayDateHour = ParamUtil.getInteger(
366             uploadReq, "displayDateHour");
367         int displayDateMinute = ParamUtil.getInteger(
368             uploadReq, "displayDateMinute");
369         int displayDateAmPm = ParamUtil.getInteger(
370             uploadReq, "displayDateAmPm");
371 
372         if (displayDateAmPm == Calendar.PM) {
373             displayDateHour += 12;
374         }
375 
376         int expirationDateMonth = ParamUtil.getInteger(
377             uploadReq, "expirationDateMonth");
378         int expirationDateDay = ParamUtil.getInteger(
379             uploadReq, "expirationDateDay");
380         int expirationDateYear = ParamUtil.getInteger(
381             uploadReq, "expirationDateYear");
382         int expirationDateHour = ParamUtil.getInteger(
383             uploadReq, "expirationDateHour");
384         int expirationDateMinute = ParamUtil.getInteger(
385             uploadReq, "expirationDateMinute");
386         int expirationDateAmPm = ParamUtil.getInteger(
387             uploadReq, "expirationDateAmPm");
388         boolean neverExpire = ParamUtil.getBoolean(uploadReq, "neverExpire");
389 
390         if (expirationDateAmPm == Calendar.PM) {
391             expirationDateHour += 12;
392         }
393 
394         int reviewDateMonth = ParamUtil.getInteger(
395             uploadReq, "reviewDateMonth");
396         int reviewDateDay = ParamUtil.getInteger(uploadReq, "reviewDateDay");
397         int reviewDateYear = ParamUtil.getInteger(uploadReq, "reviewDateYear");
398         int reviewDateHour = ParamUtil.getInteger(uploadReq, "reviewDateHour");
399         int reviewDateMinute = ParamUtil.getInteger(
400             uploadReq, "reviewDateMinute");
401         int reviewDateAmPm = ParamUtil.getInteger(uploadReq, "reviewDateAmPm");
402         boolean neverReview = ParamUtil.getBoolean(uploadReq, "neverReview");
403 
404         if (reviewDateAmPm == Calendar.PM) {
405             reviewDateHour += 12;
406         }
407 
408         boolean indexable = ParamUtil.getBoolean(uploadReq, "indexable");
409 
410         boolean smallImage = ParamUtil.getBoolean(uploadReq, "smallImage");
411         String smallImageURL = ParamUtil.getString(uploadReq, "smallImageURL");
412         File smallFile = uploadReq.getFile("smallFile");
413 
414         Map images = getImages(uploadReq);
415 
416         String articleURL = ParamUtil.getString(uploadReq, "articleURL");
417 
418         String[] tagsEntries = StringUtil.split(
419             ParamUtil.getString(uploadReq, "tagsEntries"));
420 
421         String[] communityPermissions = uploadReq.getParameterValues(
422             "communityPermissions");
423         String[] guestPermissions = uploadReq.getParameterValues(
424             "guestPermissions");
425 
426         boolean approve = ParamUtil.getBoolean(uploadReq, "approve");
427 
428         JournalArticle article = null;
429 
430         if (cmd.equals(Constants.ADD)) {
431             if (Validator.isNull(structureId)) {
432                 content = LocalizationUtil.updateLocalization(
433                     StringPool.BLANK, "static-content", content,
434                     lastLanguageId, defaultLanguageId, true);
435             }
436 
437             // Add article
438 
439             article = JournalArticleServiceUtil.addArticle(
440                 articleId, autoArticleId, layout.getPlid(), title, description,
441                 content, type, structureId, templateId, displayDateMonth,
442                 displayDateDay, displayDateYear, displayDateHour,
443                 displayDateMinute, expirationDateMonth, expirationDateDay,
444                 expirationDateYear, expirationDateHour, expirationDateMinute,
445                 neverExpire, reviewDateMonth, reviewDateDay, reviewDateYear,
446                 reviewDateHour, reviewDateMinute, neverReview, indexable,
447                 smallImage, smallImageURL, smallFile, images, articleURL,
448                 req.getPreferences(), tagsEntries, communityPermissions,
449                 guestPermissions);
450 
451             AssetPublisherUtil.addAndStoreSelection(
452                 req, JournalArticle.class.getName(), article.getPrimaryKey(),
453                 -1);
454         }
455         else {
456 
457             // Merge current content with new content
458 
459             JournalArticle curArticle = JournalArticleServiceUtil.getArticle(
460                 groupId, articleId, version);
461 
462             if (Validator.isNull(structureId)) {
463                 if (!curArticle.isTemplateDriven()) {
464                     content = LocalizationUtil.updateLocalization(
465                         curArticle.getContent(), "static-content", content,
466                         lastLanguageId, defaultLanguageId, true);
467                 }
468             }
469             else {
470                 if (curArticle.isTemplateDriven()) {
471                     JournalStructure structure =
472                         JournalStructureLocalServiceUtil.getStructure(
473                             groupId, structureId);
474 
475                     content = JournalUtil.mergeLocaleContent(
476                         curArticle.getContent(), content, structure.getXsd());
477                     content = JournalUtil.removeOldContent(
478                         content, structure.getXsd());
479                 }
480             }
481 
482             // Update article
483 
484             article = JournalArticleServiceUtil.updateArticle(
485                 groupId, articleId, version, incrementVersion, title,
486                 description, content, type, structureId, templateId,
487                 displayDateMonth, displayDateDay, displayDateYear,
488                 displayDateHour, displayDateMinute, expirationDateMonth,
489                 expirationDateDay, expirationDateYear, expirationDateHour,
490                 expirationDateMinute, neverExpire, reviewDateMonth,
491                 reviewDateDay, reviewDateYear, reviewDateHour, reviewDateMinute,
492                 neverReview, indexable, smallImage, smallImageURL, smallFile,
493                 images, articleURL, req.getPreferences(), tagsEntries);
494         }
495 
496         if (approve) {
497             article = JournalArticleServiceUtil.approveArticle(
498                 article.getGroupId(), article.getArticleId(),
499                 article.getVersion(), layout.getPlid(), articleURL,
500                 req.getPreferences());
501         }
502 
503         // Recent articles
504 
505         JournalUtil.addRecentArticle(req, article);
506 
507         // Journal content
508 
509         String portletResource = ParamUtil.getString(
510             uploadReq, "portletResource");
511 
512         if (Validator.isNotNull(portletResource)) {
513             PortletPreferences prefs =
514                 PortletPreferencesFactoryUtil.getPortletSetup(
515                     uploadReq, portletResource, true, true);
516 
517             prefs.setValue("group-id", String.valueOf(article.getGroupId()));
518             prefs.setValue("article-id", article.getArticleId());
519 
520             prefs.store();
521 
522             updateContentSearch(req, portletResource, article.getArticleId());
523         }
524 
525         return article;
526     }
527 
528     protected void updateContentSearch(
529             ActionRequest req, String portletResource, String articleId)
530         throws Exception {
531 
532         ThemeDisplay themeDisplay =
533             (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);
534 
535         Layout layout = themeDisplay.getLayout();
536 
537         JournalContentSearchLocalServiceUtil.updateContentSearch(
538             layout.getGroupId(), layout.isPrivateLayout(), layout.getLayoutId(),
539             portletResource, articleId);
540     }
541 
542 }