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