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