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.UploadRequestUtil;
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.PortletRequest;
77  import javax.portlet.RenderRequest;
78  import javax.portlet.RenderResponse;
79  import javax.portlet.WindowState;
80  
81  import org.apache.struts.action.ActionForm;
82  import org.apache.struts.action.ActionForward;
83  import org.apache.struts.action.ActionMapping;
84  
85  /**
86   * <a href="EditArticleAction.java.html"><b><i>View Source</i></b></a>
87   *
88   * @author Brian Wing Shun Chan
89   *
90   */
91  public class EditArticleAction extends PortletAction {
92  
93      public static final String VERSION_SEPARATOR = "_version_";
94  
95      public void processAction(
96              ActionMapping mapping, ActionForm form, PortletConfig config,
97              ActionRequest req, ActionResponse res)
98          throws Exception {
99  
100         String cmd = ParamUtil.getString(req, Constants.CMD);
101 
102         JournalArticle article = null;
103 
104         try {
105             if (cmd.equals(Constants.ADD) || cmd.equals(Constants.UPDATE)) {
106                 article = updateArticle(req);
107             }
108             else if (cmd.equals(Constants.APPROVE)) {
109                 approveArticle(req);
110             }
111             else if (cmd.equals(Constants.DELETE)) {
112                 deleteArticles(req);
113             }
114             else if (cmd.equals(Constants.EXPIRE)) {
115                 expireArticles(req);
116             }
117             else if (cmd.equals("removeArticlesLocale")) {
118                 removeArticlesLocale(req);
119             }
120 
121             if (Validator.isNotNull(cmd)) {
122                 String redirect = ParamUtil.getString(req, "redirect");
123 
124                 if (article != null) {
125                     boolean saveAndContinue = ParamUtil.getBoolean(
126                         req, "saveAndContinue");
127 
128                     if (saveAndContinue) {
129                         redirect = getSaveAndContinueRedirect(
130                             config, req, article, redirect);
131                     }
132                 }
133 
134                 sendRedirect(req, res, redirect);
135             }
136         }
137         catch (Exception e) {
138             if (e instanceof NoSuchArticleException ||
139                 e instanceof NoSuchStructureException ||
140                 e instanceof NoSuchTemplateException ||
141                 e instanceof PrincipalException) {
142 
143                 SessionErrors.add(req, e.getClass().getName());
144 
145                 setForward(req, "portlet.journal.error");
146             }
147             else if (e instanceof ArticleContentException ||
148                      e instanceof ArticleDisplayDateException ||
149                      e instanceof ArticleExpirationDateException ||
150                      e instanceof ArticleIdException ||
151                      e instanceof ArticleSmallImageNameException ||
152                      e instanceof ArticleSmallImageSizeException ||
153                      e instanceof ArticleTitleException ||
154                      e instanceof ArticleTypeException ||
155                      e instanceof DuplicateArticleIdException) {
156 
157                 SessionErrors.add(req, e.getClass().getName());
158             }
159             else if (e instanceof TagsEntryException) {
160                 SessionErrors.add(req, e.getClass().getName(), e);
161             }
162             else {
163                 throw e;
164             }
165         }
166     }
167 
168     public ActionForward render(
169             ActionMapping mapping, ActionForm form, PortletConfig config,
170             RenderRequest req, RenderResponse res)
171         throws Exception {
172 
173         try {
174             String cmd = ParamUtil.getString(req, Constants.CMD);
175 
176             if (!cmd.equals(Constants.ADD)) {
177                 ActionUtil.getArticle(req);
178             }
179         }
180         catch (NoSuchArticleException nsse) {
181 
182             // Let this slide because the user can manually input a article id
183             // for a new article that does not yet exist.
184 
185         }
186         catch (Exception e) {
187             if (//e instanceof NoSuchArticleException ||
188                 e instanceof PrincipalException) {
189 
190                 SessionErrors.add(req, e.getClass().getName());
191 
192                 return mapping.findForward("portlet.journal.error");
193             }
194             else {
195                 throw e;
196             }
197         }
198 
199         return mapping.findForward(
200             getForward(req, "portlet.journal.edit_article"));
201     }
202 
203     protected void approveArticle(ActionRequest req) throws Exception {
204         Layout layout = (Layout)req.getAttribute(WebKeys.LAYOUT);
205 
206         long groupId = ParamUtil.getLong(req, "groupId");
207         String articleId = ParamUtil.getString(req, "articleId");
208         double version = ParamUtil.getDouble(req, "version");
209 
210         String articleURL = ParamUtil.getString(req, "articleURL");
211 
212         JournalArticleServiceUtil.approveArticle(
213             groupId, articleId, version, layout.getPlid(), articleURL,
214             req.getPreferences());
215     }
216 
217     protected void deleteArticles(ActionRequest req) throws Exception {
218         long groupId = ParamUtil.getLong(req, "groupId");
219 
220         String[] deleteArticleIds = StringUtil.split(
221             ParamUtil.getString(req, "deleteArticleIds"));
222 
223         for (int i = 0; i < deleteArticleIds.length; i++) {
224             int pos = deleteArticleIds[i].lastIndexOf(VERSION_SEPARATOR);
225 
226             String articleId = deleteArticleIds[i].substring(0, pos);
227             double version = GetterUtil.getDouble(
228                 deleteArticleIds[i].substring(
229                     pos + VERSION_SEPARATOR.length()));
230 
231             String articleURL = ParamUtil.getString(req, "articleURL");
232 
233             JournalArticleServiceUtil.deleteArticle(
234                 groupId, articleId, version, articleURL, req.getPreferences());
235 
236             JournalUtil.removeRecentArticle(req, deleteArticleIds[i]);
237         }
238     }
239 
240     protected void expireArticles(ActionRequest req) throws Exception {
241         long groupId = ParamUtil.getLong(req, "groupId");
242 
243         String[] expireArticleIds = StringUtil.split(
244             ParamUtil.getString(req, "expireArticleIds"));
245 
246         for (int i = 0; i < expireArticleIds.length; i++) {
247             int pos = expireArticleIds[i].lastIndexOf(VERSION_SEPARATOR);
248 
249             String articleId = expireArticleIds[i].substring(0, pos);
250             double version = GetterUtil.getDouble(
251                 expireArticleIds[i].substring(
252                     pos + VERSION_SEPARATOR.length()));
253 
254             String articleURL = ParamUtil.getString(req, "articleURL");
255 
256             JournalArticleServiceUtil.expireArticle(
257                 groupId, articleId, version, articleURL, req.getPreferences());
258         }
259     }
260 
261     protected Map<String, byte[]> getImages(UploadPortletRequest uploadReq)
262         throws Exception {
263 
264         Map<String, byte[]> images = new HashMap<String, byte[]>();
265 
266         String imagePrefix = "structure_image_";
267 
268         Enumeration<String> enu = uploadReq.getParameterNames();
269 
270         while (enu.hasMoreElements()) {
271             String name = enu.nextElement();
272 
273             if (name.startsWith(imagePrefix)) {
274                 File file = uploadReq.getFile(name);
275                 byte[] bytes = FileUtil.getBytes(file);
276 
277                 if ((bytes != null) && (bytes.length > 0)) {
278                     name = name.substring(imagePrefix.length(), name.length());
279 
280                     images.put(name, bytes);
281                 }
282             }
283         }
284 
285         return images;
286     }
287 
288     protected String getSaveAndContinueRedirect(
289             PortletConfig config, ActionRequest req, JournalArticle article,
290             String redirect)
291         throws Exception {
292 
293         ThemeDisplay themeDisplay = (ThemeDisplay)req.getAttribute(
294             WebKeys.THEME_DISPLAY);
295 
296         String originalRedirect = ParamUtil.getString(req, "originalRedirect");
297 
298         PortletURLImpl portletURL = new PortletURLImpl(
299             (ActionRequestImpl)req, config.getPortletName(),
300             themeDisplay.getPlid(), PortletRequest.RENDER_PHASE);
301 
302         portletURL.setWindowState(WindowState.MAXIMIZED);
303 
304         portletURL.setParameter("struts_action", "/journal/edit_article");
305         portletURL.setParameter(Constants.CMD, Constants.UPDATE, false);
306         portletURL.setParameter("redirect", redirect, false);
307         portletURL.setParameter("originalRedirect", originalRedirect, false);
308         portletURL.setParameter(
309             "groupId", String.valueOf(article.getGroupId()), false);
310         portletURL.setParameter("articleId", article.getArticleId(), false);
311         portletURL.setParameter(
312             "version", String.valueOf(article.getVersion()), false);
313 
314         return portletURL.toString();
315     }
316 
317     protected void removeArticlesLocale(ActionRequest req) throws Exception {
318         long groupId = ParamUtil.getLong(req, "groupId");
319 
320         String[] removeArticleLocaleIds = StringUtil.split(
321             ParamUtil.getString(req, "deleteArticleIds"));
322 
323         for (int i = 0; i < removeArticleLocaleIds.length; i++) {
324             int pos = removeArticleLocaleIds[i].lastIndexOf(VERSION_SEPARATOR);
325 
326             String articleId = removeArticleLocaleIds[i].substring(0, pos);
327             double version = GetterUtil.getDouble(
328                 removeArticleLocaleIds[i].substring(
329                     pos + VERSION_SEPARATOR.length()));
330             String languageId = ParamUtil.getString(req, "languageId");
331 
332             JournalArticleServiceUtil.removeArticleLocale(
333                 groupId, articleId, version, languageId);
334         }
335     }
336 
337     protected JournalArticle updateArticle(ActionRequest req) throws Exception {
338         UploadPortletRequest uploadReq =
339             UploadRequestUtil.getUploadPortletRequest(req);
340 
341         String cmd = ParamUtil.getString(uploadReq, Constants.CMD);
342 
343         Layout layout = (Layout)uploadReq.getAttribute(WebKeys.LAYOUT);
344 
345         long groupId = ParamUtil.getLong(uploadReq, "groupId");
346 
347         String articleId = ParamUtil.getString(uploadReq, "articleId");
348         boolean autoArticleId = ParamUtil.getBoolean(
349             uploadReq, "autoArticleId");
350 
351         double version = ParamUtil.getDouble(uploadReq, "version");
352         boolean incrementVersion = ParamUtil.getBoolean(
353             uploadReq, "incrementVersion");
354 
355         String title = ParamUtil.getString(uploadReq, "title");
356         String description = ParamUtil.getString(uploadReq, "description");
357         String content = ParamUtil.getString(uploadReq, "content");
358         String type = ParamUtil.getString(uploadReq, "type");
359         String structureId = ParamUtil.getString(uploadReq, "structureId");
360         String templateId = ParamUtil.getString(uploadReq, "templateId");
361 
362         String lastLanguageId = ParamUtil.getString(
363             uploadReq, "lastLanguageId");
364         String defaultLanguageId = ParamUtil.getString(
365             uploadReq, "defaultLanguageId");
366 
367         int displayDateMonth = ParamUtil.getInteger(
368             uploadReq, "displayDateMonth");
369         int displayDateDay = ParamUtil.getInteger(uploadReq, "displayDateDay");
370         int displayDateYear = ParamUtil.getInteger(
371             uploadReq, "displayDateYear");
372         int displayDateHour = ParamUtil.getInteger(
373             uploadReq, "displayDateHour");
374         int displayDateMinute = ParamUtil.getInteger(
375             uploadReq, "displayDateMinute");
376         int displayDateAmPm = ParamUtil.getInteger(
377             uploadReq, "displayDateAmPm");
378 
379         if (displayDateAmPm == Calendar.PM) {
380             displayDateHour += 12;
381         }
382 
383         int expirationDateMonth = ParamUtil.getInteger(
384             uploadReq, "expirationDateMonth");
385         int expirationDateDay = ParamUtil.getInteger(
386             uploadReq, "expirationDateDay");
387         int expirationDateYear = ParamUtil.getInteger(
388             uploadReq, "expirationDateYear");
389         int expirationDateHour = ParamUtil.getInteger(
390             uploadReq, "expirationDateHour");
391         int expirationDateMinute = ParamUtil.getInteger(
392             uploadReq, "expirationDateMinute");
393         int expirationDateAmPm = ParamUtil.getInteger(
394             uploadReq, "expirationDateAmPm");
395         boolean neverExpire = ParamUtil.getBoolean(uploadReq, "neverExpire");
396 
397         if (expirationDateAmPm == Calendar.PM) {
398             expirationDateHour += 12;
399         }
400 
401         int reviewDateMonth = ParamUtil.getInteger(
402             uploadReq, "reviewDateMonth");
403         int reviewDateDay = ParamUtil.getInteger(uploadReq, "reviewDateDay");
404         int reviewDateYear = ParamUtil.getInteger(uploadReq, "reviewDateYear");
405         int reviewDateHour = ParamUtil.getInteger(uploadReq, "reviewDateHour");
406         int reviewDateMinute = ParamUtil.getInteger(
407             uploadReq, "reviewDateMinute");
408         int reviewDateAmPm = ParamUtil.getInteger(uploadReq, "reviewDateAmPm");
409         boolean neverReview = ParamUtil.getBoolean(uploadReq, "neverReview");
410 
411         if (reviewDateAmPm == Calendar.PM) {
412             reviewDateHour += 12;
413         }
414 
415         boolean indexable = ParamUtil.getBoolean(uploadReq, "indexable");
416 
417         boolean smallImage = ParamUtil.getBoolean(uploadReq, "smallImage");
418         String smallImageURL = ParamUtil.getString(uploadReq, "smallImageURL");
419         File smallFile = uploadReq.getFile("smallFile");
420 
421         Map<String, byte[]> images = getImages(uploadReq);
422 
423         String articleURL = ParamUtil.getString(uploadReq, "articleURL");
424 
425         String[] tagsEntries = StringUtil.split(
426             ParamUtil.getString(uploadReq, "tagsEntries"));
427 
428         String[] communityPermissions = uploadReq.getParameterValues(
429             "communityPermissions");
430         String[] guestPermissions = uploadReq.getParameterValues(
431             "guestPermissions");
432 
433         boolean approve = ParamUtil.getBoolean(uploadReq, "approve");
434 
435         JournalArticle article = null;
436 
437         if (cmd.equals(Constants.ADD)) {
438             if (Validator.isNull(structureId)) {
439                 content = LocalizationUtil.updateLocalization(
440                     StringPool.BLANK, "static-content", content,
441                     lastLanguageId, defaultLanguageId, true);
442             }
443 
444             // Add article
445 
446             article = JournalArticleServiceUtil.addArticle(
447                 articleId, autoArticleId, layout.getPlid(), title, description,
448                 content, type, structureId, templateId, displayDateMonth,
449                 displayDateDay, displayDateYear, displayDateHour,
450                 displayDateMinute, expirationDateMonth, expirationDateDay,
451                 expirationDateYear, expirationDateHour, expirationDateMinute,
452                 neverExpire, reviewDateMonth, reviewDateDay, reviewDateYear,
453                 reviewDateHour, reviewDateMinute, neverReview, indexable,
454                 smallImage, smallImageURL, smallFile, images, articleURL,
455                 req.getPreferences(), tagsEntries, communityPermissions,
456                 guestPermissions);
457 
458             AssetPublisherUtil.addAndStoreSelection(
459                 req, JournalArticle.class.getName(),
460                 article.getResourcePrimKey(), -1);
461         }
462         else {
463 
464             // Merge current content with new content
465 
466             JournalArticle curArticle = JournalArticleServiceUtil.getArticle(
467                 groupId, articleId, version);
468 
469             if (Validator.isNull(structureId)) {
470                 if (!curArticle.isTemplateDriven()) {
471                     content = LocalizationUtil.updateLocalization(
472                         curArticle.getContent(), "static-content", content,
473                         lastLanguageId, defaultLanguageId, true);
474                 }
475             }
476             else {
477                 if (curArticle.isTemplateDriven()) {
478                     JournalStructure structure =
479                         JournalStructureLocalServiceUtil.getStructure(
480                             groupId, structureId);
481 
482                     content = JournalUtil.mergeLocaleContent(
483                         curArticle.getContent(), content, structure.getXsd());
484                     content = JournalUtil.removeOldContent(
485                         content, structure.getXsd());
486                 }
487             }
488 
489             // Update article
490 
491             article = JournalArticleServiceUtil.updateArticle(
492                 groupId, articleId, version, incrementVersion, title,
493                 description, content, type, structureId, templateId,
494                 displayDateMonth, displayDateDay, displayDateYear,
495                 displayDateHour, displayDateMinute, expirationDateMonth,
496                 expirationDateDay, expirationDateYear, expirationDateHour,
497                 expirationDateMinute, neverExpire, reviewDateMonth,
498                 reviewDateDay, reviewDateYear, reviewDateHour, reviewDateMinute,
499                 neverReview, indexable, smallImage, smallImageURL, smallFile,
500                 images, articleURL, req.getPreferences(), tagsEntries);
501         }
502 
503         if (approve) {
504             article = JournalArticleServiceUtil.approveArticle(
505                 article.getGroupId(), article.getArticleId(),
506                 article.getVersion(), layout.getPlid(), articleURL,
507                 req.getPreferences());
508         }
509 
510         // Recent articles
511 
512         JournalUtil.addRecentArticle(req, article);
513 
514         // Journal content
515 
516         String portletResource = ParamUtil.getString(
517             uploadReq, "portletResource");
518 
519         if (Validator.isNotNull(portletResource)) {
520             PortletPreferences prefs =
521                 PortletPreferencesFactoryUtil.getPortletSetup(
522                     uploadReq, portletResource);
523 
524             prefs.setValue("group-id", String.valueOf(article.getGroupId()));
525             prefs.setValue("article-id", article.getArticleId());
526 
527             prefs.store();
528 
529             updateContentSearch(req, portletResource, article.getArticleId());
530         }
531 
532         return article;
533     }
534 
535     protected void updateContentSearch(
536             ActionRequest req, String portletResource, String articleId)
537         throws Exception {
538 
539         ThemeDisplay themeDisplay =
540             (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);
541 
542         Layout layout = themeDisplay.getLayout();
543 
544         JournalContentSearchLocalServiceUtil.updateContentSearch(
545             layout.getGroupId(), layout.isPrivateLayout(), layout.getLayoutId(),
546             portletResource, articleId);
547     }
548 
549 }