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