001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.blogs.service.impl;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryDefinition;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.repository.model.FileEntry;
021    import com.liferay.portal.kernel.servlet.taglib.ui.ImageSelector;
022    import com.liferay.portal.kernel.util.HtmlUtil;
023    import com.liferay.portal.kernel.util.MimeTypesUtil;
024    import com.liferay.portal.kernel.util.StringBundler;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.TempFileEntryUtil;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.workflow.WorkflowConstants;
030    import com.liferay.portal.model.Company;
031    import com.liferay.portal.model.Group;
032    import com.liferay.portal.model.Organization;
033    import com.liferay.portal.security.permission.ActionKeys;
034    import com.liferay.portal.service.ServiceContext;
035    import com.liferay.portal.theme.ThemeDisplay;
036    import com.liferay.portal.util.PortalUtil;
037    import com.liferay.portal.util.PropsValues;
038    import com.liferay.portlet.blogs.model.BlogsEntry;
039    import com.liferay.portlet.blogs.service.base.BlogsEntryServiceBaseImpl;
040    import com.liferay.portlet.blogs.service.permission.BlogsEntryPermission;
041    import com.liferay.portlet.blogs.service.permission.BlogsPermission;
042    import com.liferay.portlet.blogs.util.comparator.EntryDisplayDateComparator;
043    import com.liferay.util.RSSUtil;
044    
045    import com.sun.syndication.feed.synd.SyndContent;
046    import com.sun.syndication.feed.synd.SyndContentImpl;
047    import com.sun.syndication.feed.synd.SyndEntry;
048    import com.sun.syndication.feed.synd.SyndEntryImpl;
049    import com.sun.syndication.feed.synd.SyndFeed;
050    import com.sun.syndication.feed.synd.SyndFeedImpl;
051    import com.sun.syndication.feed.synd.SyndLink;
052    import com.sun.syndication.feed.synd.SyndLinkImpl;
053    import com.sun.syndication.io.FeedException;
054    
055    import java.io.InputStream;
056    
057    import java.util.ArrayList;
058    import java.util.Date;
059    import java.util.List;
060    
061    /**
062     * Provides the remote service for accessing, adding, deleting, subscription
063     * handling of, trash handling of, and updating blog entries. Its methods
064     * include permission checks.
065     *
066     * @author Brian Wing Shun Chan
067     * @author Mate Thurzo
068     */
069    public class BlogsEntryServiceImpl extends BlogsEntryServiceBaseImpl {
070    
071            /**
072             * @deprecated As of 7.0.0, replaced by {@link #addEntry(String, String,
073             *             String, String, int, int, int, int, int, boolean, boolean,
074             *             String[], String, ImageSelector, ImageSelector,
075             *             ServiceContext)}
076             */
077            @Deprecated
078            @Override
079            public BlogsEntry addEntry(
080                            String title, String description, String content,
081                            int displayDateMonth, int displayDateDay, int displayDateYear,
082                            int displayDateHour, int displayDateMinute, boolean allowPingbacks,
083                            boolean allowTrackbacks, String[] trackbacks, boolean smallImage,
084                            String smallImageURL, String smallImageFileName,
085                            InputStream smallImageInputStream, ServiceContext serviceContext)
086                    throws PortalException {
087    
088                    BlogsPermission.check(
089                            getPermissionChecker(), serviceContext.getScopeGroupId(),
090                            ActionKeys.ADD_ENTRY);
091    
092                    ImageSelector coverImageImageSelector = null;
093                    ImageSelector smallImageImageSelector = null;
094    
095                    if (smallImage && Validator.isNotNull(smallImageFileName) &&
096                            (smallImageInputStream != null)) {
097    
098                            FileEntry fileEntry = TempFileEntryUtil.addTempFileEntry(
099                                    serviceContext.getScopeGroupId(), getUserId(),
100                                    BlogsEntry.class.getName(), smallImageFileName,
101                                    smallImageInputStream,
102                                    MimeTypesUtil.getContentType(smallImageFileName));
103    
104                            smallImageImageSelector = new ImageSelector(
105                                    fileEntry.getFileEntryId(), smallImageURL, null);
106                    }
107    
108                    return addEntry(
109                            title, StringPool.BLANK, description, content, displayDateMonth,
110                            displayDateDay, displayDateYear, displayDateHour, displayDateMinute,
111                            allowPingbacks, allowTrackbacks, trackbacks, StringPool.BLANK,
112                            coverImageImageSelector, smallImageImageSelector, serviceContext);
113            }
114    
115            @Override
116            public BlogsEntry addEntry(
117                            String title, String subtitle, String description, String content,
118                            int displayDateMonth, int displayDateDay, int displayDateYear,
119                            int displayDateHour, int displayDateMinute, boolean allowPingbacks,
120                            boolean allowTrackbacks, String[] trackbacks,
121                            String coverImageCaption, ImageSelector coverImageImageSelector,
122                            ImageSelector smallImageImageSelector,
123                            ServiceContext serviceContext)
124                    throws PortalException {
125    
126                    BlogsPermission.check(
127                            getPermissionChecker(), serviceContext.getScopeGroupId(),
128                            ActionKeys.ADD_ENTRY);
129    
130                    return blogsEntryLocalService.addEntry(
131                            getUserId(), title, subtitle, description, content,
132                            displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
133                            displayDateMinute, allowPingbacks, allowTrackbacks, trackbacks,
134                            coverImageCaption, coverImageImageSelector, smallImageImageSelector,
135                            serviceContext);
136            }
137    
138            @Override
139            public void deleteEntry(long entryId) throws PortalException {
140                    BlogsEntryPermission.check(
141                            getPermissionChecker(), entryId, ActionKeys.DELETE);
142    
143                    blogsEntryLocalService.deleteEntry(entryId);
144            }
145    
146            @Override
147            public List<BlogsEntry> getCompanyEntries(
148                            long companyId, Date displayDate, int status, int max)
149                    throws PortalException {
150    
151                    List<BlogsEntry> entries = new ArrayList<>();
152    
153                    boolean listNotExhausted = true;
154    
155                    QueryDefinition<BlogsEntry> queryDefinition = new QueryDefinition<>(
156                            status, false, 0, 0, new EntryDisplayDateComparator());
157    
158                    if (status == WorkflowConstants.STATUS_ANY) {
159                            queryDefinition.setStatus(WorkflowConstants.STATUS_IN_TRASH, true);
160                    }
161    
162                    while ((entries.size() < max) && listNotExhausted) {
163                            queryDefinition.setEnd(queryDefinition.getStart() + max);
164    
165                            List<BlogsEntry> entryList =
166                                    blogsEntryLocalService.getCompanyEntries(
167                                            companyId, displayDate, queryDefinition);
168    
169                            queryDefinition.setStart(queryDefinition.getStart() + max);
170    
171                            listNotExhausted = (entryList.size() == max);
172    
173                            for (BlogsEntry entry : entryList) {
174                                    if (entries.size() >= max) {
175                                            break;
176                                    }
177    
178                                    if (BlogsEntryPermission.contains(
179                                                    getPermissionChecker(), entry, ActionKeys.VIEW)) {
180    
181                                            entries.add(entry);
182                                    }
183                            }
184                    }
185    
186                    return entries;
187            }
188    
189            @Override
190            public String getCompanyEntriesRSS(
191                            long companyId, Date displayDate, int status, int max, String type,
192                            double version, String displayStyle, String feedURL,
193                            String entryURL, ThemeDisplay themeDisplay)
194                    throws PortalException {
195    
196                    Company company = companyPersistence.findByPrimaryKey(companyId);
197    
198                    String name = company.getName();
199                    List<BlogsEntry> blogsEntries = getCompanyEntries(
200                            companyId, displayDate, status, max);
201    
202                    return exportToRSS(
203                            name, name, type, version, displayStyle, feedURL, entryURL,
204                            blogsEntries, themeDisplay);
205            }
206    
207            @Override
208            public BlogsEntry getEntry(long entryId) throws PortalException {
209                    BlogsEntryPermission.check(
210                            getPermissionChecker(), entryId, ActionKeys.VIEW);
211    
212                    return blogsEntryLocalService.getEntry(entryId);
213            }
214    
215            @Override
216            public BlogsEntry getEntry(long groupId, String urlTitle)
217                    throws PortalException {
218    
219                    BlogsEntry entry = blogsEntryLocalService.getEntry(groupId, urlTitle);
220    
221                    BlogsEntryPermission.check(
222                            getPermissionChecker(), entry.getEntryId(), ActionKeys.VIEW);
223    
224                    return entry;
225            }
226    
227            @Override
228            public List<BlogsEntry> getGroupEntries(
229                    long groupId, Date displayDate, int status, int max) {
230    
231                    return getGroupEntries(groupId, displayDate, status, 0, max);
232            }
233    
234            @Override
235            public List<BlogsEntry> getGroupEntries(
236                    long groupId, Date displayDate, int status, int start, int end) {
237    
238                    if (status == WorkflowConstants.STATUS_ANY) {
239                            return blogsEntryPersistence.filterFindByG_LtD_NotS(
240                                    groupId, displayDate, WorkflowConstants.STATUS_IN_TRASH, start,
241                                    end);
242                    }
243                    else {
244                            return blogsEntryPersistence.filterFindByG_LtD_S(
245                                    groupId, displayDate, status, start, end);
246                    }
247            }
248    
249            @Override
250            public List<BlogsEntry> getGroupEntries(long groupId, int status, int max) {
251                    return getGroupEntries(groupId, status, 0, max);
252            }
253    
254            @Override
255            public List<BlogsEntry> getGroupEntries(
256                    long groupId, int status, int start, int end) {
257    
258                    if (status == WorkflowConstants.STATUS_ANY) {
259                            return blogsEntryPersistence.filterFindByG_NotS(
260                                    groupId, WorkflowConstants.STATUS_IN_TRASH, start, end);
261                    }
262                    else {
263                            return blogsEntryPersistence.filterFindByG_S(
264                                    groupId, status, start, end);
265                    }
266            }
267    
268            @Override
269            public int getGroupEntriesCount(
270                    long groupId, Date displayDate, int status) {
271    
272                    if (status == WorkflowConstants.STATUS_ANY) {
273                            return blogsEntryPersistence.filterCountByG_LtD_NotS(
274                                    groupId, displayDate, WorkflowConstants.STATUS_IN_TRASH);
275                    }
276                    else {
277                            return blogsEntryPersistence.filterCountByG_LtD_S(
278                                    groupId, displayDate, status);
279                    }
280            }
281    
282            @Override
283            public int getGroupEntriesCount(long groupId, int status) {
284                    if (status == WorkflowConstants.STATUS_ANY) {
285                            return blogsEntryPersistence.filterCountByG_NotS(
286                                    groupId, WorkflowConstants.STATUS_IN_TRASH);
287                    }
288                    else {
289                            return blogsEntryPersistence.filterCountByG_S(groupId, status);
290                    }
291            }
292    
293            @Override
294            public String getGroupEntriesRSS(
295                            long groupId, Date displayDate, int status, int max, String type,
296                            double version, String displayStyle, String feedURL,
297                            String entryURL, ThemeDisplay themeDisplay)
298                    throws PortalException {
299    
300                    Group group = groupPersistence.findByPrimaryKey(groupId);
301    
302                    String name = group.getDescriptiveName();
303                    List<BlogsEntry> blogsEntries = getGroupEntries(
304                            groupId, displayDate, status, max);
305    
306                    return exportToRSS(
307                            name, name, type, version, displayStyle, feedURL, entryURL,
308                            blogsEntries, themeDisplay);
309            }
310    
311            @Override
312            public List<BlogsEntry> getGroupsEntries(
313                            long companyId, long groupId, Date displayDate, int status, int max)
314                    throws PortalException {
315    
316                    List<BlogsEntry> entries = new ArrayList<>();
317    
318                    boolean listNotExhausted = true;
319    
320                    QueryDefinition<BlogsEntry> queryDefinition = new QueryDefinition<>(
321                            status, false, 0, 0, new EntryDisplayDateComparator());
322    
323                    if (status == WorkflowConstants.STATUS_ANY) {
324                            queryDefinition.setStatus(WorkflowConstants.STATUS_IN_TRASH, true);
325                    }
326    
327                    while ((entries.size() < max) && listNotExhausted) {
328                            queryDefinition.setEnd(queryDefinition.getStart() + max);
329    
330                            List<BlogsEntry> entryList =
331                                    blogsEntryLocalService.getGroupsEntries(
332                                            companyId, groupId, displayDate, queryDefinition);
333    
334                            queryDefinition.setStart(queryDefinition.getStart() + max);
335    
336                            listNotExhausted = (entryList.size() == max);
337    
338                            for (BlogsEntry entry : entryList) {
339                                    if (entries.size() >= max) {
340                                            break;
341                                    }
342    
343                                    if (BlogsEntryPermission.contains(
344                                                    getPermissionChecker(), entry, ActionKeys.VIEW)) {
345    
346                                            entries.add(entry);
347                                    }
348                            }
349                    }
350    
351                    return entries;
352            }
353    
354            @Override
355            public List<BlogsEntry> getOrganizationEntries(
356                            long organizationId, Date displayDate, int status, int max)
357                    throws PortalException {
358    
359                    List<BlogsEntry> entries = new ArrayList<>();
360    
361                    boolean listNotExhausted = true;
362    
363                    QueryDefinition<BlogsEntry> queryDefinition = new QueryDefinition<>(
364                            status, false, 0, 0, new EntryDisplayDateComparator());
365    
366                    if (status == WorkflowConstants.STATUS_ANY) {
367                            queryDefinition.setStatus(WorkflowConstants.STATUS_IN_TRASH, true);
368                    }
369    
370                    while ((entries.size() < max) && listNotExhausted) {
371                            queryDefinition.setEnd(queryDefinition.getStart() + max);
372    
373                            List<BlogsEntry> entryList = blogsEntryFinder.findByOrganizationId(
374                                    organizationId, displayDate, queryDefinition);
375    
376                            queryDefinition.setStart(queryDefinition.getStart() + max);
377    
378                            listNotExhausted = (entryList.size() == max);
379    
380                            for (BlogsEntry entry : entryList) {
381                                    if (entries.size() >= max) {
382                                            break;
383                                    }
384    
385                                    if (BlogsEntryPermission.contains(
386                                                    getPermissionChecker(), entry, ActionKeys.VIEW)) {
387    
388                                            entries.add(entry);
389                                    }
390                            }
391                    }
392    
393                    return entries;
394            }
395    
396            @Override
397            public String getOrganizationEntriesRSS(
398                            long organizationId, Date displayDate, int status, int max,
399                            String type, double version, String displayStyle, String feedURL,
400                            String entryURL, ThemeDisplay themeDisplay)
401                    throws PortalException {
402    
403                    Organization organization = organizationPersistence.findByPrimaryKey(
404                            organizationId);
405    
406                    String name = organization.getName();
407                    List<BlogsEntry> blogsEntries = getOrganizationEntries(
408                            organizationId, displayDate, status, max);
409    
410                    return exportToRSS(
411                            name, name, type, version, displayStyle, feedURL, entryURL,
412                            blogsEntries, themeDisplay);
413            }
414    
415            @Override
416            public BlogsEntry moveEntryToTrash(long entryId) throws PortalException {
417                    BlogsEntryPermission.check(
418                            getPermissionChecker(), entryId, ActionKeys.DELETE);
419    
420                    return blogsEntryLocalService.moveEntryToTrash(getUserId(), entryId);
421            }
422    
423            @Override
424            public void restoreEntryFromTrash(long entryId) throws PortalException {
425                    BlogsEntryPermission.check(
426                            getPermissionChecker(), entryId, ActionKeys.DELETE);
427    
428                    blogsEntryLocalService.restoreEntryFromTrash(getUserId(), entryId);
429            }
430    
431            @Override
432            public void subscribe(long groupId) throws PortalException {
433                    BlogsPermission.check(
434                            getPermissionChecker(), groupId, ActionKeys.SUBSCRIBE);
435    
436                    blogsEntryLocalService.subscribe(getUserId(), groupId);
437            }
438    
439            @Override
440            public void unsubscribe(long groupId) throws PortalException {
441                    BlogsPermission.check(
442                            getPermissionChecker(), groupId, ActionKeys.SUBSCRIBE);
443    
444                    blogsEntryLocalService.unsubscribe(getUserId(), groupId);
445            }
446    
447            /**
448             * @deprecated As of 7.0.0, replaced by {@link #updateEntry(long, String,
449             *             String, String, String, int, int, int, int, int, boolean,
450             *             boolean, String[], String, ImageSelector, ImageSelector,
451             *             ServiceContext)}
452             */
453            @Deprecated
454            @Override
455            public BlogsEntry updateEntry(
456                            long entryId, String title, String description, String content,
457                            int displayDateMonth, int displayDateDay, int displayDateYear,
458                            int displayDateHour, int displayDateMinute, boolean allowPingbacks,
459                            boolean allowTrackbacks, String[] trackbacks, boolean smallImage,
460                            String smallImageURL, String smallImageFileName,
461                            InputStream smallImageInputStream, ServiceContext serviceContext)
462                    throws PortalException {
463    
464                    BlogsPermission.check(
465                            getPermissionChecker(), serviceContext.getScopeGroupId(),
466                            ActionKeys.UPDATE);
467    
468                    ImageSelector coverImageImageSelector = null;
469                    ImageSelector smallImageImageSelector = null;
470    
471                    if (smallImage) {
472                            if (Validator.isNotNull(smallImageFileName) &&
473                                    (smallImageInputStream != null)) {
474    
475                                    FileEntry fileEntry = TempFileEntryUtil.addTempFileEntry(
476                                            serviceContext.getScopeGroupId(), getUserId(),
477                                            BlogsEntry.class.getName(), smallImageFileName,
478                                            smallImageInputStream,
479                                            MimeTypesUtil.getContentType(smallImageFileName));
480    
481                                    smallImageImageSelector = new ImageSelector(
482                                            fileEntry.getFileEntryId(), smallImageURL, null);
483                            }
484                    }
485                    else {
486                            smallImageImageSelector = new ImageSelector(0);
487                    }
488    
489                    return updateEntry(
490                            entryId, title, StringPool.BLANK, description, content,
491                            displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
492                            displayDateMinute, allowPingbacks, allowTrackbacks, trackbacks,
493                            StringPool.BLANK, coverImageImageSelector, smallImageImageSelector,
494                            serviceContext);
495            }
496    
497            @Override
498            public BlogsEntry updateEntry(
499                            long entryId, String title, String subtitle, String description,
500                            String content, int displayDateMonth, int displayDateDay,
501                            int displayDateYear, int displayDateHour, int displayDateMinute,
502                            boolean allowPingbacks, boolean allowTrackbacks,
503                            String[] trackbacks, String coverImageCaption,
504                            ImageSelector coverImageImageSelector,
505                            ImageSelector smallImageImageSelector,
506                            ServiceContext serviceContext)
507                    throws PortalException {
508    
509                    BlogsEntryPermission.check(
510                            getPermissionChecker(), entryId, ActionKeys.UPDATE);
511    
512                    return blogsEntryLocalService.updateEntry(
513                            getUserId(), entryId, title, subtitle, description, content,
514                            displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
515                            displayDateMinute, allowPingbacks, allowTrackbacks, trackbacks,
516                            coverImageCaption, coverImageImageSelector, smallImageImageSelector,
517                            serviceContext);
518            }
519    
520            protected String exportToRSS(
521                    String name, String description, String type, double version,
522                    String displayStyle, String feedURL, String entryURL,
523                    List<BlogsEntry> blogsEntries, ThemeDisplay themeDisplay) {
524    
525                    SyndFeed syndFeed = new SyndFeedImpl();
526    
527                    syndFeed.setDescription(description);
528    
529                    List<SyndEntry> syndEntries = new ArrayList<>();
530    
531                    syndFeed.setEntries(syndEntries);
532    
533                    for (BlogsEntry entry : blogsEntries) {
534                            SyndEntry syndEntry = new SyndEntryImpl();
535    
536                            String author = PortalUtil.getUserName(entry);
537    
538                            syndEntry.setAuthor(author);
539    
540                            SyndContent syndContent = new SyndContentImpl();
541    
542                            syndContent.setType(RSSUtil.ENTRY_TYPE_DEFAULT);
543    
544                            String value = null;
545    
546                            if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
547                                    String summary = entry.getDescription();
548    
549                                    if (Validator.isNull(summary)) {
550                                            summary = entry.getContent();
551                                    }
552    
553                                    value = StringUtil.shorten(
554                                            HtmlUtil.extractText(summary),
555                                            PropsValues.BLOGS_RSS_ABSTRACT_LENGTH, StringPool.BLANK);
556                            }
557                            else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
558                                    value = StringPool.BLANK;
559                            }
560                            else {
561                                    value = StringUtil.replace(
562                                            entry.getContent(), new String[] {"href=\"/", "src=\"/"},
563                                            new String[] {
564                                                    "href=\"" + themeDisplay.getURLPortal() + "/",
565                                                    "src=\"" + themeDisplay.getURLPortal() + "/"
566                                            });
567                            }
568    
569                            syndContent.setValue(value);
570    
571                            syndEntry.setDescription(syndContent);
572    
573                            StringBundler sb = new StringBundler(4);
574    
575                            if (entryURL.endsWith("/blogs/rss")) {
576                                    sb.append(entryURL.substring(0, entryURL.length() - 3));
577                                    sb.append(entry.getUrlTitle());
578                            }
579                            else {
580                                    sb.append(entryURL);
581    
582                                    if (!entryURL.endsWith(StringPool.QUESTION)) {
583                                            sb.append(StringPool.AMPERSAND);
584                                    }
585    
586                                    sb.append("entryId=");
587                                    sb.append(entry.getEntryId());
588                            }
589    
590                            String link = sb.toString();
591    
592                            syndEntry.setLink(link);
593    
594                            syndEntry.setPublishedDate(entry.getDisplayDate());
595                            syndEntry.setTitle(entry.getTitle());
596                            syndEntry.setUpdatedDate(entry.getModifiedDate());
597                            syndEntry.setUri(link);
598    
599                            syndEntries.add(syndEntry);
600                    }
601    
602                    syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
603    
604                    List<SyndLink> syndLinks = new ArrayList<>();
605    
606                    syndFeed.setLinks(syndLinks);
607    
608                    SyndLink selfSyndLink = new SyndLinkImpl();
609    
610                    syndLinks.add(selfSyndLink);
611    
612                    selfSyndLink.setHref(feedURL);
613                    selfSyndLink.setRel("self");
614    
615                    if (feedURL.endsWith("/-/blogs/rss")) {
616                            SyndLink alternateSyndLink = new SyndLinkImpl();
617    
618                            syndLinks.add(alternateSyndLink);
619    
620                            alternateSyndLink.setHref(
621                                    feedURL.substring(0, feedURL.length() - 12));
622                            alternateSyndLink.setRel("alternate");
623                    }
624    
625                    syndFeed.setPublishedDate(new Date());
626                    syndFeed.setTitle(name);
627                    syndFeed.setUri(feedURL);
628    
629                    try {
630                            return RSSUtil.export(syndFeed);
631                    }
632                    catch (FeedException fe) {
633                            throw new SystemException(fe);
634                    }
635            }
636    
637    }