001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.blogs.service.impl;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.json.JSONFactoryUtil;
020    import com.liferay.portal.kernel.json.JSONObject;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.search.Indexer;
024    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
025    import com.liferay.portal.kernel.util.Constants;
026    import com.liferay.portal.kernel.util.ContentTypes;
027    import com.liferay.portal.kernel.util.FileUtil;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.HtmlUtil;
030    import com.liferay.portal.kernel.util.HttpUtil;
031    import com.liferay.portal.kernel.util.OrderByComparator;
032    import com.liferay.portal.kernel.util.PropsKeys;
033    import com.liferay.portal.kernel.util.SetUtil;
034    import com.liferay.portal.kernel.util.StringBundler;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.kernel.util.StringUtil;
037    import com.liferay.portal.kernel.util.Validator;
038    import com.liferay.portal.kernel.workflow.WorkflowConstants;
039    import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
040    import com.liferay.portal.model.Group;
041    import com.liferay.portal.model.ResourceConstants;
042    import com.liferay.portal.model.User;
043    import com.liferay.portal.service.ServiceContext;
044    import com.liferay.portal.service.ServiceContextUtil;
045    import com.liferay.portal.util.Portal;
046    import com.liferay.portal.util.PortalUtil;
047    import com.liferay.portal.util.PortletKeys;
048    import com.liferay.portal.util.PrefsPropsUtil;
049    import com.liferay.portal.util.PropsValues;
050    import com.liferay.portal.util.SubscriptionSender;
051    import com.liferay.portlet.asset.model.AssetEntry;
052    import com.liferay.portlet.asset.model.AssetLinkConstants;
053    import com.liferay.portlet.blogs.EntryContentException;
054    import com.liferay.portlet.blogs.EntryDisplayDateException;
055    import com.liferay.portlet.blogs.EntrySmallImageNameException;
056    import com.liferay.portlet.blogs.EntrySmallImageSizeException;
057    import com.liferay.portlet.blogs.EntryTitleException;
058    import com.liferay.portlet.blogs.model.BlogsEntry;
059    import com.liferay.portlet.blogs.service.base.BlogsEntryLocalServiceBaseImpl;
060    import com.liferay.portlet.blogs.social.BlogsActivityKeys;
061    import com.liferay.portlet.blogs.util.BlogsUtil;
062    import com.liferay.portlet.blogs.util.LinkbackProducerUtil;
063    import com.liferay.portlet.blogs.util.comparator.EntryDisplayDateComparator;
064    
065    import java.io.IOException;
066    import java.io.InputStream;
067    
068    import java.util.Date;
069    import java.util.HashMap;
070    import java.util.HashSet;
071    import java.util.List;
072    import java.util.Locale;
073    import java.util.Map;
074    import java.util.Set;
075    import java.util.regex.Matcher;
076    import java.util.regex.Pattern;
077    
078    import javax.portlet.PortletPreferences;
079    
080    import net.htmlparser.jericho.Source;
081    import net.htmlparser.jericho.StartTag;
082    
083    /**
084     * @author Brian Wing Shun Chan
085     * @author Wilson S. Man
086     * @author Raymond Augé
087     * @author Thiago Moreira
088     * @author Juan Fernández
089     * @author Zsolt Berentey
090     */
091    public class BlogsEntryLocalServiceImpl extends BlogsEntryLocalServiceBaseImpl {
092    
093            public BlogsEntry addEntry(
094                            long userId, String title, String description, String content,
095                            int displayDateMonth, int displayDateDay, int displayDateYear,
096                            int displayDateHour, int displayDateMinute, boolean allowPingbacks,
097                            boolean allowTrackbacks, String[] trackbacks, boolean smallImage,
098                            String smallImageURL, String smallImageFileName,
099                            InputStream smallImageInputStream, ServiceContext serviceContext)
100                    throws PortalException, SystemException {
101    
102                    // Entry
103    
104                    User user = userPersistence.findByPrimaryKey(userId);
105                    long groupId = serviceContext.getScopeGroupId();
106    
107                    Date displayDate = PortalUtil.getDate(
108                            displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
109                            displayDateMinute, user.getTimeZone(),
110                            EntryDisplayDateException.class);
111    
112                    byte[] smallImageBytes = null;
113    
114                    try {
115                            if ((smallImageInputStream != null) && smallImage) {
116                                    smallImageBytes = FileUtil.getBytes(smallImageInputStream);
117                            }
118                    }
119                    catch (IOException ioe) {
120                    }
121    
122                    Date now = new Date();
123    
124                    validate(
125                            title, content, smallImage, smallImageURL, smallImageFileName,
126                            smallImageBytes);
127    
128                    long entryId = counterLocalService.increment();
129    
130                    BlogsEntry entry = blogsEntryPersistence.create(entryId);
131    
132                    entry.setUuid(serviceContext.getUuid());
133                    entry.setGroupId(groupId);
134                    entry.setCompanyId(user.getCompanyId());
135                    entry.setUserId(user.getUserId());
136                    entry.setUserName(user.getFullName());
137                    entry.setCreateDate(serviceContext.getCreateDate(now));
138                    entry.setModifiedDate(serviceContext.getModifiedDate(now));
139                    entry.setTitle(title);
140                    entry.setUrlTitle(
141                            getUniqueUrlTitle(entryId, title, null, serviceContext));
142                    entry.setDescription(description);
143                    entry.setContent(content);
144                    entry.setDisplayDate(displayDate);
145                    entry.setAllowPingbacks(allowPingbacks);
146                    entry.setAllowTrackbacks(allowTrackbacks);
147                    entry.setSmallImage(smallImage);
148                    entry.setSmallImageId(counterLocalService.increment());
149                    entry.setSmallImageURL(smallImageURL);
150                    entry.setStatus(WorkflowConstants.STATUS_DRAFT);
151                    entry.setStatusDate(serviceContext.getModifiedDate(now));
152                    entry.setExpandoBridgeAttributes(serviceContext);
153    
154                    blogsEntryPersistence.update(entry, false);
155    
156                    // Resources
157    
158                    if (serviceContext.isAddGroupPermissions() ||
159                            serviceContext.isAddGuestPermissions()) {
160    
161                            addEntryResources(
162                                    entry, serviceContext.isAddGroupPermissions(),
163                                    serviceContext.isAddGuestPermissions());
164                    }
165                    else {
166                            addEntryResources(
167                                    entry, serviceContext.getGroupPermissions(),
168                                    serviceContext.getGuestPermissions());
169                    }
170    
171                    // Small image
172    
173                    saveImages(smallImage, entry.getSmallImageId(), smallImageBytes);
174    
175                    // Asset
176    
177                    updateAsset(
178                            userId, entry, serviceContext.getAssetCategoryIds(),
179                            serviceContext.getAssetTagNames(),
180                            serviceContext.getAssetLinkEntryIds());
181    
182                    // Message boards
183    
184                    if (PropsValues.BLOGS_ENTRY_COMMENTS_ENABLED) {
185                            mbMessageLocalService.addDiscussionMessage(
186                                    userId, entry.getUserName(), groupId,
187                                    BlogsEntry.class.getName(), entryId,
188                                    WorkflowConstants.ACTION_PUBLISH);
189                    }
190    
191                    // Workflow
192    
193                    if ((trackbacks != null) && (trackbacks.length > 0)) {
194                            serviceContext.setAttribute("trackbacks", trackbacks);
195                    }
196                    else {
197                            serviceContext.setAttribute("trackbacks", null);
198                    }
199    
200                    WorkflowHandlerRegistryUtil.startWorkflowInstance(
201                            user.getCompanyId(), groupId, userId, BlogsEntry.class.getName(),
202                            entry.getEntryId(), entry, serviceContext);
203    
204                    return entry;
205            }
206    
207            public void addEntryResources(
208                            BlogsEntry entry, boolean addGroupPermissions,
209                            boolean addGuestPermissions)
210                    throws PortalException, SystemException {
211    
212                    resourceLocalService.addResources(
213                            entry.getCompanyId(), entry.getGroupId(), entry.getUserId(),
214                            BlogsEntry.class.getName(), entry.getEntryId(), false,
215                            addGroupPermissions, addGuestPermissions);
216            }
217    
218            public void addEntryResources(
219                            BlogsEntry entry, String[] groupPermissions,
220                            String[] guestPermissions)
221                    throws PortalException, SystemException {
222    
223                    resourceLocalService.addModelResources(
224                            entry.getCompanyId(), entry.getGroupId(), entry.getUserId(),
225                            BlogsEntry.class.getName(), entry.getEntryId(), groupPermissions,
226                            guestPermissions);
227            }
228    
229            public void addEntryResources(
230                            long entryId, boolean addGroupPermissions,
231                            boolean addGuestPermissions)
232                    throws PortalException, SystemException {
233    
234                    BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
235    
236                    addEntryResources(entry, addGroupPermissions, addGuestPermissions);
237            }
238    
239            public void addEntryResources(
240                            long entryId, String[] groupPermissions, String[] guestPermissions)
241                    throws PortalException, SystemException {
242    
243                    BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
244    
245                    addEntryResources(entry, groupPermissions, guestPermissions);
246            }
247    
248            public void checkEntries() throws PortalException, SystemException {
249                    Date now = new Date();
250    
251                    int count = blogsEntryPersistence.countByLtD_S(
252                            now, WorkflowConstants.STATUS_SCHEDULED);
253    
254                    if (count == 0) {
255                            return;
256                    }
257    
258                    List<BlogsEntry> entries = blogsEntryPersistence.findByLtD_S(
259                            now, WorkflowConstants.STATUS_SCHEDULED);
260    
261                    for (BlogsEntry entry : entries) {
262                            ServiceContext serviceContext = new ServiceContext();
263    
264                            String[] trackbacks = StringUtil.split(entry.getTrackbacks());
265    
266                            serviceContext.setAttribute("trackbacks", trackbacks);
267    
268                            serviceContext.setCommand(Constants.UPDATE);
269    
270                            String layoutFullURL = PortalUtil.getLayoutFullURL(
271                                    entry.getGroupId(), PortletKeys.BLOGS);
272    
273                            serviceContext.setLayoutFullURL(layoutFullURL);
274    
275                            serviceContext.setScopeGroupId(entry.getGroupId());
276    
277                            updateStatus(
278                                    entry.getStatusByUserId(), entry.getEntryId(),
279                                    WorkflowConstants.STATUS_APPROVED, serviceContext);
280                    }
281            }
282    
283            public void deleteEntries(long groupId)
284                    throws PortalException, SystemException {
285    
286                    for (BlogsEntry entry : blogsEntryPersistence.findByGroupId(groupId)) {
287                            deleteEntry(entry);
288                    }
289            }
290    
291            public void deleteEntry(BlogsEntry entry)
292                    throws PortalException, SystemException {
293    
294                    // Entry
295    
296                    blogsEntryPersistence.remove(entry);
297    
298                    // Resources
299    
300                    resourceLocalService.deleteResource(
301                            entry.getCompanyId(), BlogsEntry.class.getName(),
302                            ResourceConstants.SCOPE_INDIVIDUAL, entry.getEntryId());
303    
304                    // Image
305    
306                    imageLocalService.deleteImage(entry.getSmallImageId());
307    
308                    // Subscriptions
309    
310                    subscriptionLocalService.deleteSubscriptions(
311                            entry.getCompanyId(), BlogsEntry.class.getName(),
312                            entry.getEntryId());
313    
314                    // Statistics
315    
316                    blogsStatsUserLocalService.updateStatsUser(
317                            entry.getGroupId(), entry.getUserId());
318    
319                    // Asset
320    
321                    assetEntryLocalService.deleteEntry(
322                            BlogsEntry.class.getName(), entry.getEntryId());
323    
324                    // Expando
325    
326                    expandoValueLocalService.deleteValues(
327                            BlogsEntry.class.getName(), entry.getEntryId());
328    
329                    // Message boards
330    
331                    mbMessageLocalService.deleteDiscussionMessages(
332                            BlogsEntry.class.getName(), entry.getEntryId());
333    
334                    // Ratings
335    
336                    ratingsStatsLocalService.deleteStats(
337                            BlogsEntry.class.getName(), entry.getEntryId());
338    
339                    // Indexer
340    
341                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
342                            BlogsEntry.class);
343    
344                    indexer.delete(entry);
345    
346                    // Workflow
347    
348                    workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(
349                            entry.getCompanyId(), entry.getGroupId(),
350                            BlogsEntry.class.getName(), entry.getEntryId());
351            }
352    
353            public void deleteEntry(long entryId)
354                    throws PortalException, SystemException {
355    
356                    BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
357    
358                    deleteEntry(entry);
359            }
360    
361            public List<BlogsEntry> getCompanyEntries(
362                            long companyId, Date displayDate, int status, int start, int end)
363                    throws SystemException {
364    
365                    if (status == WorkflowConstants.STATUS_ANY) {
366                            return blogsEntryPersistence.findByC_LtD(
367                                    companyId, displayDate, start, end);
368                    }
369                    else {
370                            return blogsEntryPersistence.findByC_LtD_S(
371                                    companyId, displayDate, status, start, end);
372                    }
373            }
374    
375            public List<BlogsEntry> getCompanyEntries(
376                            long companyId, Date displayDate, int status, int start, int end,
377                            OrderByComparator obc)
378                    throws SystemException {
379    
380                    if (status == WorkflowConstants.STATUS_ANY) {
381                            return blogsEntryPersistence.findByC_LtD(
382                                    companyId, displayDate, start, end, obc);
383                    }
384                    else {
385                            return blogsEntryPersistence.findByC_LtD_S(
386                                    companyId, displayDate, status, start, end, obc);
387                    }
388            }
389    
390            public int getCompanyEntriesCount(
391                            long companyId, Date displayDate, int status)
392                    throws SystemException {
393    
394                    if (status == WorkflowConstants.STATUS_ANY) {
395                            return blogsEntryPersistence.countByC_LtD(companyId, displayDate);
396                    }
397                    else {
398                            return blogsEntryPersistence.countByC_LtD_S(
399                                    companyId, displayDate, status);
400                    }
401            }
402    
403            public BlogsEntry[] getEntriesPrevAndNext(long entryId)
404                    throws PortalException, SystemException {
405    
406                    BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
407    
408                    return blogsEntryPersistence.findByG_S_PrevAndNext(
409                            entry.getEntryId(), entry.getGroupId(),
410                            WorkflowConstants.STATUS_APPROVED,
411                            new EntryDisplayDateComparator(true));
412            }
413    
414            public BlogsEntry getEntry(long entryId)
415                    throws PortalException, SystemException {
416    
417                    return blogsEntryPersistence.findByPrimaryKey(entryId);
418            }
419    
420            public BlogsEntry getEntry(long groupId, String urlTitle)
421                    throws PortalException, SystemException {
422    
423                    return blogsEntryPersistence.findByG_UT(groupId, urlTitle);
424            }
425    
426            public List<BlogsEntry> getGroupEntries(
427                            long groupId, Date displayDate, int status, int start, int end)
428                    throws SystemException {
429    
430                    if (status == WorkflowConstants.STATUS_ANY) {
431                            return blogsEntryPersistence.findByG_LtD(
432                                    groupId, displayDate, start, end);
433                    }
434                    else {
435                            return blogsEntryPersistence.findByG_LtD_S(
436                                    groupId, displayDate, status, start, end);
437                    }
438            }
439    
440            public List<BlogsEntry> getGroupEntries(
441                            long groupId, Date displayDate, int status, int start, int end,
442                            OrderByComparator obc)
443                    throws SystemException {
444    
445                    if (status == WorkflowConstants.STATUS_ANY) {
446                            return blogsEntryPersistence.findByG_LtD(
447                                    groupId, displayDate, start, end, obc);
448                    }
449                    else {
450                            return blogsEntryPersistence.findByG_LtD_S(
451                                    groupId, displayDate, status, start, end, obc);
452                    }
453            }
454    
455            public List<BlogsEntry> getGroupEntries(
456                            long groupId, int status, int start, int end)
457                    throws SystemException {
458    
459                    if (status == WorkflowConstants.STATUS_ANY) {
460                            return blogsEntryPersistence.findByGroupId(groupId, start, end);
461                    }
462                    else {
463                            return blogsEntryPersistence.findByG_S(groupId, status, start, end);
464                    }
465            }
466    
467            public List<BlogsEntry> getGroupEntries(
468                            long groupId, int status, int start, int end, OrderByComparator obc)
469                    throws SystemException {
470    
471                    if (status == WorkflowConstants.STATUS_ANY) {
472                            return blogsEntryPersistence.findByGroupId(
473                                    groupId, start, end, obc);
474                    }
475                    else {
476                            return blogsEntryPersistence.findByG_S(
477                                    groupId, status, start, end, obc);
478                    }
479            }
480    
481            public int getGroupEntriesCount(long groupId, Date displayDate, int status)
482                    throws SystemException {
483    
484                    if (status == WorkflowConstants.STATUS_ANY) {
485                            return blogsEntryPersistence.countByG_LtD(groupId, displayDate);
486                    }
487                    else {
488                            return blogsEntryPersistence.countByG_LtD_S(
489                                    groupId, displayDate, status);
490                    }
491            }
492    
493            public int getGroupEntriesCount(long groupId, int status)
494                    throws SystemException {
495    
496                    if (status == WorkflowConstants.STATUS_ANY) {
497                            return blogsEntryPersistence.countByGroupId(groupId);
498                    }
499                    else {
500                            return blogsEntryPersistence.countByG_S(groupId, status);
501                    }
502            }
503    
504            public List<BlogsEntry> getGroupsEntries(
505                            long companyId, long groupId, Date displayDate, int status,
506                            int start, int end)
507                    throws SystemException {
508    
509                    return blogsEntryFinder.findByGroupIds(
510                            companyId, groupId, displayDate, status, start, end);
511            }
512    
513            public List<BlogsEntry> getGroupUserEntries(
514                            long groupId, long userId, Date displayDate, int status, int start,
515                            int end)
516                    throws SystemException {
517    
518                    if (status == WorkflowConstants.STATUS_ANY) {
519                            return blogsEntryPersistence.findByG_U_LtD(
520                                    groupId, userId, displayDate, start, end);
521                    }
522                    else {
523                            return blogsEntryPersistence.findByG_U_LtD_S(
524                                    groupId, userId, displayDate, status, start, end);
525                    }
526            }
527    
528            public List<BlogsEntry> getGroupUserEntries(
529                            long groupId, long userId, Date displayDate, int status, int start,
530                            int end, OrderByComparator obc)
531                    throws SystemException {
532    
533                    if (status == WorkflowConstants.STATUS_ANY) {
534                            return blogsEntryPersistence.findByG_U_LtD(
535                                    groupId, userId, displayDate, start, end, obc);
536                    }
537                    else {
538                            return blogsEntryPersistence.findByG_U_LtD_S(
539                                    groupId, userId, displayDate, status, start, end, obc);
540                    }
541            }
542    
543            public int getGroupUserEntriesCount(
544                            long groupId, long userId, Date displayDate, int status)
545                    throws SystemException {
546    
547                    if (status == WorkflowConstants.STATUS_ANY) {
548                            return blogsEntryPersistence.countByG_U_LtD(
549                                    groupId, userId, displayDate);
550                    }
551                    else {
552                            return blogsEntryPersistence.countByG_U_LtD_S(
553                                    groupId, userId, displayDate, status);
554                    }
555            }
556    
557            public List<BlogsEntry> getNoAssetEntries() throws SystemException {
558                    return blogsEntryFinder.findByNoAssets();
559            }
560    
561            public List<BlogsEntry> getOrganizationEntries(
562                            long organizationId, Date displayDate, int status, int start,
563                            int end)
564                    throws SystemException {
565    
566                    return blogsEntryFinder.findByOrganizationId(
567                            organizationId, displayDate, status, start, end, null);
568            }
569    
570            public List<BlogsEntry> getOrganizationEntries(
571                            long organizationId, Date displayDate, int status, int start,
572                            int end, OrderByComparator obc)
573                    throws SystemException {
574    
575                    return blogsEntryFinder.findByOrganizationId(
576                            organizationId, displayDate, status, start, end, obc);
577            }
578    
579            public int getOrganizationEntriesCount(
580                            long organizationId, Date displayDate, int status)
581                    throws SystemException {
582    
583                    return blogsEntryFinder.countByOrganizationId(
584                            organizationId, displayDate, status);
585            }
586    
587            public void subscribe(long userId, long groupId)
588                    throws PortalException, SystemException {
589    
590                    subscriptionLocalService.addSubscription(
591                            userId, groupId, BlogsEntry.class.getName(), groupId);
592            }
593    
594            public void unsubscribe(long userId, long groupId)
595                    throws PortalException, SystemException {
596    
597                    subscriptionLocalService.deleteSubscription(
598                            userId, BlogsEntry.class.getName(), groupId);
599            }
600    
601            public void updateAsset(
602                            long userId, BlogsEntry entry, long[] assetCategoryIds,
603                            String[] assetTagNames, long[] assetLinkEntryIds)
604                    throws PortalException, SystemException {
605    
606                    boolean visible = false;
607    
608                    if (entry.isApproved()) {
609                            visible = true;
610                    }
611    
612                    String summary = HtmlUtil.extractText(
613                            StringUtil.shorten(entry.getContent(), 500));
614    
615                    AssetEntry assetEntry = assetEntryLocalService.updateEntry(
616                            userId, entry.getGroupId(), BlogsEntry.class.getName(),
617                            entry.getEntryId(), entry.getUuid(), 0, assetCategoryIds,
618                            assetTagNames, visible, null, null, entry.getDisplayDate(), null,
619                            ContentTypes.TEXT_HTML, entry.getTitle(), null, summary, null, null,
620                            0, 0, null, false);
621    
622                    assetLinkLocalService.updateLinks(
623                            userId, assetEntry.getEntryId(), assetLinkEntryIds,
624                            AssetLinkConstants.TYPE_RELATED);
625            }
626    
627            public BlogsEntry updateEntry(
628                            long userId, long entryId, String title, String description,
629                            String content, int displayDateMonth, int displayDateDay,
630                            int displayDateYear, int displayDateHour, int displayDateMinute,
631                            boolean allowPingbacks, boolean allowTrackbacks,
632                            String[] trackbacks, boolean smallImage, String smallImageURL,
633                            String smallImageFileName, InputStream smallImageInputStream,
634                            ServiceContext serviceContext)
635                    throws PortalException, SystemException {
636    
637                    // Entry
638    
639                    User user = userPersistence.findByPrimaryKey(userId);
640    
641                    Date displayDate = PortalUtil.getDate(
642                            displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
643                            displayDateMinute, user.getTimeZone(),
644                            EntryDisplayDateException.class);
645    
646                    byte[] smallImageBytes = null;
647    
648                    try {
649                            if ((smallImageInputStream != null) && smallImage) {
650                                    smallImageBytes = FileUtil.getBytes(smallImageInputStream);
651                            }
652                    }
653                    catch (IOException ioe) {
654                    }
655    
656                    validate(
657                            title, content, smallImage, smallImageURL, smallImageFileName,
658                            smallImageBytes);
659    
660                    BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
661    
662                    String oldUrlTitle = entry.getUrlTitle();
663    
664                    entry.setModifiedDate(serviceContext.getModifiedDate(null));
665                    entry.setTitle(title);
666                    entry.setUrlTitle(
667                            getUniqueUrlTitle(entryId, title, oldUrlTitle, serviceContext));
668                    entry.setDescription(description);
669                    entry.setContent(content);
670                    entry.setDisplayDate(displayDate);
671                    entry.setAllowPingbacks(allowPingbacks);
672                    entry.setAllowTrackbacks(allowTrackbacks);
673                    entry.setSmallImage(smallImage);
674    
675                    if (entry.getSmallImageId() == 0) {
676                            entry.setSmallImageId(counterLocalService.increment());
677                    }
678    
679                    entry.setSmallImageURL(smallImageURL);
680    
681                    if (entry.isPending() || entry.isDraft()) {
682                    }
683                    else if (entry.isApproved()) {
684                            entry.setStatus(WorkflowConstants.STATUS_DRAFT_FROM_APPROVED);
685                    }
686                    else {
687                            entry.setStatus(WorkflowConstants.STATUS_DRAFT);
688                    }
689    
690                    entry.setExpandoBridgeAttributes(serviceContext);
691    
692                    blogsEntryPersistence.update(entry, false);
693    
694                    // Resources
695    
696                    if ((serviceContext.getGroupPermissions() != null) ||
697                            (serviceContext.getGuestPermissions() != null)) {
698    
699                            updateEntryResources(
700                                    entry, serviceContext.getGroupPermissions(),
701                                    serviceContext.getGuestPermissions());
702                    }
703    
704                    // Small image
705    
706                    saveImages(smallImage, entry.getSmallImageId(), smallImageBytes);
707    
708                    // Asset
709    
710                    updateAsset(
711                            userId, entry, serviceContext.getAssetCategoryIds(),
712                            serviceContext.getAssetTagNames(),
713                            serviceContext.getAssetLinkEntryIds());
714    
715                    // Workflow
716    
717                    boolean pingOldTrackbacks = false;
718    
719                    if (!oldUrlTitle.equals(entry.getUrlTitle())) {
720                            pingOldTrackbacks = true;
721                    }
722    
723                    serviceContext.setAttribute(
724                            "pingOldTrackbacks", String.valueOf(pingOldTrackbacks));
725    
726                    if (Validator.isNotNull(trackbacks)) {
727                            serviceContext.setAttribute("trackbacks", trackbacks);
728                    }
729                    else {
730                            serviceContext.setAttribute("trackbacks", null);
731                    }
732    
733                    WorkflowHandlerRegistryUtil.startWorkflowInstance(
734                            user.getCompanyId(), entry.getGroupId(), userId,
735                            BlogsEntry.class.getName(), entry.getEntryId(), entry,
736                            serviceContext);
737    
738                    return entry;
739            }
740    
741            public void updateEntryResources(
742                            BlogsEntry entry, String[] groupPermissions,
743                            String[] guestPermissions)
744                    throws PortalException, SystemException {
745    
746                    resourceLocalService.updateResources(
747                            entry.getCompanyId(), entry.getGroupId(),
748                            BlogsEntry.class.getName(), entry.getEntryId(), groupPermissions,
749                            guestPermissions);
750            }
751    
752            public BlogsEntry updateStatus(
753                            long userId, long entryId, int status,
754                            ServiceContext serviceContext)
755                    throws PortalException, SystemException {
756    
757                    // Entry
758    
759                    User user = userPersistence.findByPrimaryKey(userId);
760                    Date now = new Date();
761    
762                    BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
763    
764                    int oldStatus = entry.getStatus();
765    
766                    if (oldStatus == WorkflowConstants.STATUS_DRAFT) {
767                            serviceContext.setCommand(Constants.ADD);
768                    }
769    
770                    if ((status == WorkflowConstants.STATUS_APPROVED) &&
771                            now.before(entry.getDisplayDate())) {
772    
773                            status = WorkflowConstants.STATUS_SCHEDULED;
774                    }
775    
776                    entry.setModifiedDate(serviceContext.getModifiedDate(now));
777                    entry.setStatus(status);
778                    entry.setStatusByUserId(user.getUserId());
779                    entry.setStatusByUserName(user.getFullName());
780                    entry.setStatusDate(serviceContext.getModifiedDate(now));
781    
782                    blogsEntryPersistence.update(entry, false);
783    
784                    // Statistics
785    
786                    blogsStatsUserLocalService.updateStatsUser(
787                            entry.getGroupId(), user.getUserId(), entry.getDisplayDate());
788    
789                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
790                            BlogsEntry.class);
791    
792                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();
793    
794                    extraDataJSONObject.put("title", entry.getTitle());
795    
796                    if (status == WorkflowConstants.STATUS_APPROVED) {
797    
798                            // Asset
799    
800                            assetEntryLocalService.updateVisible(
801                                    BlogsEntry.class.getName(), entryId, true);
802    
803                            // Social
804    
805                            if (oldStatus != WorkflowConstants.STATUS_SCHEDULED) {
806                                    if (serviceContext.isCommandUpdate()) {
807                                            socialActivityLocalService.addActivity(
808                                                    user.getUserId(), entry.getGroupId(),
809                                                    BlogsEntry.class.getName(), entryId,
810                                                    BlogsActivityKeys.UPDATE_ENTRY,
811                                                    extraDataJSONObject.toString(), 0);
812                                    }
813                                    else {
814                                            socialActivityLocalService.addUniqueActivity(
815                                                    user.getUserId(), entry.getGroupId(),
816                                                    BlogsEntry.class.getName(), entryId,
817                                                    BlogsActivityKeys.ADD_ENTRY,
818                                                    extraDataJSONObject.toString(), 0);
819                                    }
820                            }
821    
822                            // Indexer
823    
824                            indexer.reindex(entry);
825    
826                            // Subscriptions
827    
828                            notifySubscribers(entry, serviceContext);
829    
830                            // Ping
831    
832                            String[] trackbacks = (String[])serviceContext.getAttribute(
833                                    "trackbacks");
834                            Boolean pingOldTrackbacks = GetterUtil.getBoolean(
835                                    (String)serviceContext.getAttribute("pingOldTrackbacks"));
836    
837                            pingGoogle(entry, serviceContext);
838                            pingPingback(entry, serviceContext);
839                            pingTrackbacks(
840                                    entry, trackbacks, pingOldTrackbacks, serviceContext);
841                    }
842                    else {
843    
844                            // Asset
845    
846                            assetEntryLocalService.updateVisible(
847                                    BlogsEntry.class.getName(), entryId, false);
848    
849                            // Social
850    
851                            if (status == WorkflowConstants.STATUS_SCHEDULED) {
852                                    if (serviceContext.isCommandUpdate()) {
853                                            socialActivityLocalService.addActivity(
854                                                    user.getUserId(), entry.getGroupId(),
855                                                    BlogsEntry.class.getName(), entryId,
856                                                    BlogsActivityKeys.UPDATE_ENTRY,
857                                                    extraDataJSONObject.toString(), 0);
858                                    }
859                                    else {
860                                            socialActivityLocalService.addUniqueActivity(
861                                                    user.getUserId(), entry.getGroupId(),
862                                                    BlogsEntry.class.getName(), entryId,
863                                                    BlogsActivityKeys.ADD_ENTRY,
864                                                    extraDataJSONObject.toString(), 0);
865                                    }
866                            }
867    
868                            // Indexer
869    
870                            indexer.delete(entry);
871                    }
872    
873                    return entry;
874            }
875    
876            protected String getUniqueUrlTitle(long entryId, long groupId, String title)
877                    throws SystemException {
878    
879                    String urlTitle = BlogsUtil.getUrlTitle(entryId, title);
880    
881                    for (int i = 1;; i++) {
882                            BlogsEntry entry = blogsEntryPersistence.fetchByG_UT(
883                                    groupId, urlTitle);
884    
885                            if ((entry == null) || (entryId == entry.getEntryId())) {
886                                    break;
887                            }
888                            else {
889                                    String suffix = StringPool.DASH + i;
890    
891                                    String prefix = urlTitle;
892    
893                                    if (urlTitle.length() > suffix.length()) {
894                                            prefix = urlTitle.substring(
895                                                    0, urlTitle.length() - suffix.length());
896                                    }
897    
898                                    urlTitle = prefix + suffix;
899                            }
900                    }
901    
902                    return urlTitle;
903            }
904    
905            protected String getUniqueUrlTitle(
906                            long entryId, String title, String oldUrlTitle,
907                            ServiceContext serviceContext)
908                    throws SystemException {
909    
910                    String serviceContextUrlTitle = GetterUtil.getString(
911                            serviceContext.getAttribute("urlTitle"));
912    
913                    String urlTitle = null;
914    
915                    if (isMatchesServiceContextUrlTitle(serviceContextUrlTitle)) {
916                            urlTitle = BlogsUtil.getUrlTitle(entryId, serviceContextUrlTitle);
917    
918                            BlogsEntry urlTitleEntry = blogsEntryPersistence.fetchByG_UT(
919                                    serviceContext.getScopeGroupId(), urlTitle);
920    
921                            if ((urlTitleEntry != null) &&
922                                    (urlTitleEntry.getEntryId() != entryId)) {
923    
924                                    urlTitle = getUniqueUrlTitle(
925                                            entryId, serviceContext.getScopeGroupId(), urlTitle);
926                            }
927                    }
928                    else {
929                            if (isMatchesServiceContextUrlTitle(oldUrlTitle)) {
930                                    urlTitle = oldUrlTitle;
931                            }
932                            else {
933                                    urlTitle = getUniqueUrlTitle(
934                                            entryId, serviceContext.getScopeGroupId(), title);
935                            }
936                    }
937    
938                    return urlTitle;
939            }
940    
941            protected boolean isMatchesServiceContextUrlTitle(String urlTitle) {
942                    if (Validator.isNotNull(urlTitle) &&
943                            Validator.isNotNull(PropsValues.BLOGS_ENTRY_URL_TITLE_REGEXP)) {
944    
945                            Pattern pattern = Pattern.compile(
946                                    PropsValues.BLOGS_ENTRY_URL_TITLE_REGEXP);
947    
948                            Matcher matcher = pattern.matcher(urlTitle);
949    
950                            return matcher.matches();
951                    }
952    
953                    return false;
954            }
955    
956            protected void notifySubscribers(
957                            BlogsEntry entry, ServiceContext serviceContext)
958                    throws SystemException {
959    
960                    if (!entry.isApproved()) {
961                            return;
962                    }
963    
964                    String layoutFullURL = serviceContext.getLayoutFullURL();
965    
966                    if (Validator.isNull(layoutFullURL)) {
967                            return;
968                    }
969    
970                    PortletPreferences preferences =
971                            ServiceContextUtil.getPortletPreferences(serviceContext);
972    
973                    if (preferences == null) {
974                            long ownerId = entry.getGroupId();
975                            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
976                            long plid = PortletKeys.PREFS_PLID_SHARED;
977                            String portletId = PortletKeys.BLOGS;
978                            String defaultPreferences = null;
979    
980                            preferences = portletPreferencesLocalService.getPreferences(
981                                    entry.getCompanyId(), ownerId, ownerType, plid, portletId,
982                                    defaultPreferences);
983                    }
984    
985                    if (serviceContext.isCommandAdd() &&
986                            BlogsUtil.getEmailEntryAddedEnabled(preferences)) {
987                    }
988                    else if (serviceContext.isCommandUpdate() &&
989                                     BlogsUtil.getEmailEntryUpdatedEnabled(preferences)) {
990                    }
991                    else {
992                            return;
993                    }
994    
995                    String entryURL =
996                            layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR + "blogs" +
997                                    StringPool.SLASH + entry.getEntryId();
998    
999                    String fromName = BlogsUtil.getEmailFromName(
1000                            preferences, entry.getCompanyId());
1001                    String fromAddress = BlogsUtil.getEmailFromAddress(
1002                            preferences, entry.getCompanyId());
1003    
1004                    Map<Locale, String> localizedSubjectMap = null;
1005                    Map<Locale, String> localizedBodyMap = null;
1006    
1007                    if (serviceContext.isCommandUpdate()) {
1008                            localizedSubjectMap = BlogsUtil.getEmailEntryUpdatedSubjectMap(
1009                                    preferences);
1010                            localizedBodyMap = BlogsUtil.getEmailEntryUpdatedBodyMap(
1011                                    preferences);
1012                    }
1013                    else {
1014                            localizedSubjectMap = BlogsUtil.getEmailEntryAddedSubjectMap(
1015                                    preferences);
1016                            localizedBodyMap = BlogsUtil.getEmailEntryAddedBodyMap(preferences);
1017                    }
1018    
1019                    SubscriptionSender subscriptionSender = new SubscriptionSender();
1020    
1021                    subscriptionSender.setCompanyId(entry.getCompanyId());
1022                    subscriptionSender.setContextAttributes(
1023                            "[$BLOGS_ENTRY_STATUS_BY_USER_NAME$]", entry.getStatusByUserName(),
1024                            "[$BLOGS_ENTRY_URL$]", entryURL);
1025                    subscriptionSender.setContextUserPrefix("BLOGS_ENTRY");
1026                    subscriptionSender.setFrom(fromAddress, fromName);
1027                    subscriptionSender.setHtmlFormat(true);
1028                    subscriptionSender.setLocalizedBodyMap(localizedBodyMap);
1029                    subscriptionSender.setLocalizedSubjectMap(localizedSubjectMap);
1030                    subscriptionSender.setMailId("blogs_entry", entry.getEntryId());
1031                    subscriptionSender.setPortletId(PortletKeys.BLOGS);
1032                    subscriptionSender.setReplyToAddress(fromAddress);
1033                    subscriptionSender.setScopeGroupId(entry.getGroupId());
1034                    subscriptionSender.setServiceContext(serviceContext);
1035                    subscriptionSender.setUserId(entry.getUserId());
1036    
1037                    subscriptionSender.addPersistedSubscribers(
1038                            BlogsEntry.class.getName(), entry.getGroupId());
1039    
1040                    subscriptionSender.flushNotificationsAsync();
1041            }
1042    
1043            protected void pingGoogle(BlogsEntry entry, ServiceContext serviceContext)
1044                    throws PortalException, SystemException {
1045    
1046                    if (!PropsValues.BLOGS_PING_GOOGLE_ENABLED || !entry.isApproved()) {
1047                            return;
1048                    }
1049    
1050                    String layoutFullURL = PortalUtil.getLayoutFullURL(
1051                            serviceContext.getScopeGroupId(), PortletKeys.BLOGS);
1052    
1053                    if (Validator.isNull(layoutFullURL)) {
1054                            return;
1055                    }
1056    
1057                    if (layoutFullURL.contains("://localhost")) {
1058                            if (_log.isDebugEnabled()) {
1059                                    _log.debug(
1060                                            "Not pinging Google because of localhost URL " +
1061                                                    layoutFullURL);
1062                            }
1063    
1064                            return;
1065                    }
1066    
1067                    Group group = groupPersistence.findByPrimaryKey(entry.getGroupId());
1068    
1069                    StringBundler sb = new StringBundler(6);
1070    
1071                    String name = group.getDescriptiveName();
1072                    String url = layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR + "blogs";
1073                    String changesURL =
1074                            layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR + "blogs/rss";
1075    
1076                    sb.append("http://blogsearch.google.com/ping?name=");
1077                    sb.append(HttpUtil.encodeURL(name));
1078                    sb.append("&url=");
1079                    sb.append(HttpUtil.encodeURL(url));
1080                    sb.append("&changesURL=");
1081                    sb.append(HttpUtil.encodeURL(changesURL));
1082    
1083                    String location = sb.toString();
1084    
1085                    if (_log.isInfoEnabled()) {
1086                            _log.info("Pinging Google at " + location);
1087                    }
1088    
1089                    try {
1090                            String response = HttpUtil.URLtoString(sb.toString());
1091    
1092                            if (_log.isInfoEnabled()) {
1093                                    _log.info("Google ping response: " + response);
1094                            }
1095                    }
1096                    catch (IOException ioe) {
1097                            _log.error("Unable to ping Google at " + location, ioe);
1098                    }
1099            }
1100    
1101            protected void pingPingback(
1102                    BlogsEntry entry, ServiceContext serviceContext) {
1103    
1104                    if (!PropsValues.BLOGS_PINGBACK_ENABLED ||
1105                            !entry.isAllowPingbacks() || !entry.isApproved()) {
1106    
1107                            return;
1108                    }
1109    
1110                    String layoutFullURL = serviceContext.getLayoutFullURL();
1111    
1112                    if (Validator.isNull(layoutFullURL)) {
1113                            return;
1114                    }
1115    
1116                    String sourceUri =
1117                            layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR + "blogs/" +
1118                                    entry.getUrlTitle();
1119    
1120                    Source source = new Source(entry.getContent());
1121    
1122                    List<StartTag> tags = source.getAllStartTags("a");
1123    
1124                    for (StartTag tag : tags) {
1125                            String targetUri = tag.getAttributeValue("href");
1126    
1127                            if (Validator.isNotNull(targetUri)) {
1128                                    try {
1129                                            LinkbackProducerUtil.sendPingback(sourceUri, targetUri);
1130                                    }
1131                                    catch (Exception e) {
1132                                            _log.error("Error while sending pingback " + targetUri, e);
1133                                    }
1134                            }
1135                    }
1136            }
1137    
1138            protected void pingTrackbacks(
1139                            BlogsEntry entry, String[] trackbacks, boolean pingOldTrackbacks,
1140                            ServiceContext serviceContext)
1141                    throws SystemException {
1142    
1143                    if (!PropsValues.BLOGS_TRACKBACK_ENABLED ||
1144                            !entry.isAllowTrackbacks() || !entry.isApproved()) {
1145    
1146                            return;
1147                    }
1148    
1149                    String layoutFullURL = serviceContext.getLayoutFullURL();
1150    
1151                    if (Validator.isNull(layoutFullURL)) {
1152                            return;
1153                    }
1154    
1155                    Map<String, String> parts = new HashMap<String, String>();
1156    
1157                    String excerpt = StringUtil.shorten(
1158                            HtmlUtil.extractText(entry.getContent()),
1159                            PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH);
1160                    String url =
1161                            layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR + "blogs/" +
1162                                    entry.getUrlTitle();
1163    
1164                    parts.put("title", entry.getTitle());
1165                    parts.put("excerpt", excerpt);
1166                    parts.put("url", url);
1167                    parts.put("blog_name", entry.getUserName());
1168    
1169                    Set<String> trackbacksSet = null;
1170    
1171                    if (Validator.isNotNull(trackbacks)) {
1172                            trackbacksSet = SetUtil.fromArray(trackbacks);
1173                    }
1174                    else {
1175                            trackbacksSet = new HashSet<String>();
1176                    }
1177    
1178                    if (pingOldTrackbacks) {
1179                            trackbacksSet.addAll(
1180                                    SetUtil.fromArray(StringUtil.split(entry.getTrackbacks())));
1181    
1182                            entry.setTrackbacks(StringPool.BLANK);
1183    
1184                            blogsEntryPersistence.update(entry, false);
1185                    }
1186    
1187                    Set<String> oldTrackbacks = SetUtil.fromArray(
1188                            StringUtil.split(entry.getTrackbacks()));
1189    
1190                    Set<String> validTrackbacks = new HashSet<String>();
1191    
1192                    for (String trackback : trackbacksSet) {
1193                            if (oldTrackbacks.contains(trackback)) {
1194                                    continue;
1195                            }
1196    
1197                            try {
1198                                    if (LinkbackProducerUtil.sendTrackback(trackback, parts)) {
1199                                            validTrackbacks.add(trackback);
1200                                    }
1201                            }
1202                            catch (Exception e) {
1203                                    _log.error("Error while sending trackback at " + trackback, e);
1204                            }
1205                    }
1206    
1207                    if (!validTrackbacks.isEmpty()) {
1208                            String newTrackbacks = StringUtil.merge(validTrackbacks);
1209    
1210                            if (Validator.isNotNull(entry.getTrackbacks())) {
1211                                    newTrackbacks += StringPool.COMMA + entry.getTrackbacks();
1212                            }
1213    
1214                            entry.setTrackbacks(newTrackbacks);
1215    
1216                            blogsEntryPersistence.update(entry, false);
1217                    }
1218            }
1219    
1220            protected void saveImages(
1221                            boolean smallImage, long smallImageId, byte[] smallImageBytes)
1222                    throws PortalException, SystemException {
1223    
1224                    if (smallImage) {
1225                            if (smallImageBytes != null) {
1226                                    imageLocalService.updateImage(smallImageId, smallImageBytes);
1227                            }
1228                    }
1229                    else {
1230                            imageLocalService.deleteImage(smallImageId);
1231                    }
1232            }
1233    
1234            protected void validate(
1235                            String title, String content, boolean smallImage,
1236                            String smallImageURL, String smallImageFileName,
1237                            byte[] smallImageBytes)
1238                    throws PortalException, SystemException {
1239    
1240                    if (Validator.isNull(title)) {
1241                            throw new EntryTitleException();
1242                    }
1243                    else if (Validator.isNull(content)) {
1244                            throw new EntryContentException();
1245                    }
1246    
1247                    String[] imageExtensions = PrefsPropsUtil.getStringArray(
1248                            PropsKeys.BLOGS_IMAGE_EXTENSIONS, StringPool.COMMA);
1249    
1250                    if (smallImage && Validator.isNull(smallImageURL) &&
1251                            (smallImageBytes != null)) {
1252    
1253                            if (smallImageFileName != null) {
1254                                    boolean validSmallImageExtension = false;
1255    
1256                                    for (String _imageExtension : imageExtensions) {
1257                                            if (StringPool.STAR.equals(_imageExtension) ||
1258                                                    StringUtil.endsWith(
1259                                                            smallImageFileName, _imageExtension)) {
1260    
1261                                                    validSmallImageExtension = true;
1262    
1263                                                    break;
1264                                            }
1265                                    }
1266    
1267                                    if (!validSmallImageExtension) {
1268                                            throw new EntrySmallImageNameException(smallImageFileName);
1269                                    }
1270                            }
1271    
1272                            long smallImageMaxSize = PrefsPropsUtil.getLong(
1273                                    PropsKeys.BLOGS_IMAGE_SMALL_MAX_SIZE);
1274    
1275                            if ((smallImageMaxSize > 0) &&
1276                                    ((smallImageBytes == null) ||
1277                                     (smallImageBytes.length > smallImageMaxSize))) {
1278    
1279                                    throw new EntrySmallImageSizeException();
1280                            }
1281                    }
1282            }
1283    
1284            private static Log _log = LogFactoryUtil.getLog(
1285                    BlogsEntryLocalServiceImpl.class);
1286    
1287    }