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.util;
016    
017    import com.liferay.portal.kernel.comment.CommentManager;
018    import com.liferay.portal.kernel.comment.DuplicateCommentException;
019    import com.liferay.portal.kernel.language.LanguageUtil;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.portlet.FriendlyURLMapper;
023    import com.liferay.portal.kernel.portlet.FriendlyURLMapperThreadLocal;
024    import com.liferay.portal.kernel.util.ArrayUtil;
025    import com.liferay.portal.kernel.util.GetterUtil;
026    import com.liferay.portal.kernel.util.HttpUtil;
027    import com.liferay.portal.kernel.util.LocaleUtil;
028    import com.liferay.portal.kernel.util.StringBundler;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.StringUtil;
031    import com.liferay.portal.kernel.util.Validator;
032    import com.liferay.portal.kernel.xmlrpc.Method;
033    import com.liferay.portal.kernel.xmlrpc.Response;
034    import com.liferay.portal.kernel.xmlrpc.XmlRpcConstants;
035    import com.liferay.portal.kernel.xmlrpc.XmlRpcUtil;
036    import com.liferay.portal.model.Portlet;
037    import com.liferay.portal.service.PortletLocalServiceUtil;
038    import com.liferay.portal.service.ServiceContext;
039    import com.liferay.portal.service.UserLocalServiceUtil;
040    import com.liferay.portal.util.Portal;
041    import com.liferay.portal.util.PortalUtil;
042    import com.liferay.portal.util.PortletKeys;
043    import com.liferay.portal.util.PropsValues;
044    import com.liferay.portlet.blogs.model.BlogsEntry;
045    import com.liferay.portlet.blogs.pingback.DisabledPingbackException;
046    import com.liferay.portlet.blogs.pingback.InvalidSourceURIException;
047    import com.liferay.portlet.blogs.pingback.UnavailableSourceURIException;
048    import com.liferay.portlet.blogs.service.BlogsEntryLocalServiceUtil;
049    
050    import java.io.IOException;
051    
052    import java.net.URL;
053    
054    import java.util.HashMap;
055    import java.util.List;
056    import java.util.Map;
057    
058    import net.htmlparser.jericho.Element;
059    import net.htmlparser.jericho.Source;
060    import net.htmlparser.jericho.StartTag;
061    import net.htmlparser.jericho.TextExtractor;
062    
063    /**
064     * @author Alexander Chow
065     */
066    public class PingbackMethodImpl implements Method {
067    
068            public static final int ACCESS_DENIED = 49;
069    
070            public static final int GENERIC_FAULT = 0;
071    
072            public static final int PINGBACK_ALREADY_REGISTERED = 48;
073    
074            public static final int SERVER_ERROR = 50;
075    
076            public static final int SOURCE_URI_DOES_NOT_EXIST = 16;
077    
078            public static final int SOURCE_URI_INVALID = 17;
079    
080            public static final int TARGET_URI_DOES_NOT_EXIST = 32;
081    
082            public static final int TARGET_URI_INVALID = 33;
083    
084            @Override
085            public Response execute(long companyId) {
086                    try {
087                            addPingback(companyId);
088    
089                            return XmlRpcUtil.createSuccess("Pingback accepted");
090                    }
091                    catch (DuplicateCommentException dce) {
092                            return XmlRpcUtil.createFault(
093                                    PINGBACK_ALREADY_REGISTERED, "Pingback is already registered");
094                    }
095                    catch (InvalidSourceURIException isue) {
096                            return XmlRpcUtil.createFault(
097                                    SOURCE_URI_INVALID, isue.getMessage());
098                    }
099                    catch (DisabledPingbackException pde) {
100                            return XmlRpcUtil.createFault(
101                                    XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND, pde.getMessage());
102                    }
103                    catch (UnavailableSourceURIException usue) {
104                            return XmlRpcUtil.createFault(
105                                    SOURCE_URI_DOES_NOT_EXIST, usue.getMessage());
106                    }
107                    catch (Exception e) {
108                            if (_log.isDebugEnabled()) {
109                                    _log.debug(e, e);
110                            }
111    
112                            return XmlRpcUtil.createFault(
113                                    TARGET_URI_INVALID, "Unable to parse target URI");
114                    }
115            }
116    
117            @Override
118            public String getMethodName() {
119                    return "pingback.ping";
120            }
121    
122            @Override
123            public String getToken() {
124                    return "pingback";
125            }
126    
127            @Override
128            public boolean setArguments(Object[] arguments) {
129                    try {
130                            _sourceURI = (String)arguments[0];
131                            _targetURI = (String)arguments[1];
132    
133                            return true;
134                    }
135                    catch (Exception e) {
136                            if (_log.isDebugEnabled()) {
137                                    _log.debug(e, e);
138                            }
139    
140                            return false;
141                    }
142            }
143    
144            public void setCommentManager(CommentManager commentManager) {
145                    _commentManager = commentManager;
146            }
147    
148            protected void addPingback(long companyId) throws Exception {
149                    if (!PropsValues.BLOGS_PINGBACK_ENABLED) {
150                            throw new DisabledPingbackException("Pingbacks are disabled");
151                    }
152    
153                    validateSource();
154    
155                    BlogsEntry entry = getBlogsEntry(companyId);
156    
157                    if (!entry.isAllowPingbacks()) {
158                            throw new DisabledPingbackException("Pingbacks are disabled");
159                    }
160    
161                    long userId = UserLocalServiceUtil.getDefaultUserId(companyId);
162                    long groupId = entry.getGroupId();
163                    String className = BlogsEntry.class.getName();
164                    long classPK = entry.getEntryId();
165    
166                    String body =
167                            "[...] " + getExcerpt() + " [...] [url=" + _sourceURI + "]" +
168                                    LanguageUtil.get(LocaleUtil.getSiteDefault(), "read-more") +
169                                            "[/url]";
170    
171                    ServiceContext serviceContext = buildServiceContext(
172                            companyId, groupId, entry.getUrlTitle());
173    
174                    _commentManager.addComment(
175                            userId, groupId, className, classPK, body, serviceContext);
176            }
177    
178            protected ServiceContext buildServiceContext(
179                            long companyId, long groupId, String urlTitle)
180                    throws Exception {
181    
182                    ServiceContext serviceContext = new ServiceContext();
183    
184                    String pingbackUserName = LanguageUtil.get(
185                            LocaleUtil.getSiteDefault(), "pingback");
186    
187                    serviceContext.setAttribute("pingbackUserName", pingbackUserName);
188    
189                    StringBundler sb = new StringBundler(5);
190    
191                    String layoutFullURL = PortalUtil.getLayoutFullURL(
192                            groupId, PortletKeys.BLOGS);
193    
194                    sb.append(layoutFullURL);
195    
196                    sb.append(Portal.FRIENDLY_URL_SEPARATOR);
197    
198                    Portlet portlet = PortletLocalServiceUtil.getPortletById(
199                            companyId, PortletKeys.BLOGS);
200    
201                    sb.append(portlet.getFriendlyURLMapping());
202                    sb.append(StringPool.SLASH);
203                    sb.append(urlTitle);
204    
205                    serviceContext.setAttribute("redirect", sb.toString());
206    
207                    serviceContext.setLayoutFullURL(layoutFullURL);
208    
209                    return serviceContext;
210            }
211    
212            protected BlogsEntry getBlogsEntry(long companyId) throws Exception {
213                    BlogsEntry entry = null;
214    
215                    URL url = new URL(_targetURI);
216    
217                    String friendlyURL = url.getPath();
218    
219                    int end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
220    
221                    if (end != -1) {
222                            friendlyURL = friendlyURL.substring(0, end);
223                    }
224    
225                    long plid = PortalUtil.getPlidFromFriendlyURL(companyId, friendlyURL);
226                    long groupId = PortalUtil.getScopeGroupId(plid);
227    
228                    Map<String, String[]> params = new HashMap<String, String[]>();
229    
230                    FriendlyURLMapperThreadLocal.setPRPIdentifiers(
231                            new HashMap<String, String>());
232    
233                    Portlet portlet = PortletLocalServiceUtil.getPortletById(
234                            PortletKeys.BLOGS);
235    
236                    FriendlyURLMapper friendlyURLMapper =
237                            portlet.getFriendlyURLMapperInstance();
238    
239                    friendlyURL = url.getPath();
240    
241                    end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
242    
243                    if (end != -1) {
244                            friendlyURL = friendlyURL.substring(
245                                    end + Portal.FRIENDLY_URL_SEPARATOR.length() - 1);
246                    }
247    
248                    Map<String, Object> requestContext = new HashMap<String, Object>();
249    
250                    friendlyURLMapper.populateParams(friendlyURL, params, requestContext);
251    
252                    String param = getParam(params, "entryId");
253    
254                    if (Validator.isNotNull(param)) {
255                            long entryId = GetterUtil.getLong(param);
256    
257                            entry = BlogsEntryLocalServiceUtil.getEntry(entryId);
258                    }
259                    else {
260                            String urlTitle = getParam(params, "urlTitle");
261    
262                            entry = BlogsEntryLocalServiceUtil.getEntry(groupId, urlTitle);
263                    }
264    
265                    return entry;
266            }
267    
268            protected String getExcerpt() throws IOException {
269                    String html = HttpUtil.URLtoString(_sourceURI);
270    
271                    Source source = new Source(html);
272    
273                    source.fullSequentialParse();
274    
275                    List<Element> elements = source.getAllElements("a");
276    
277                    for (Element element : elements) {
278                            String href = GetterUtil.getString(
279                                    element.getAttributeValue("href"));
280    
281                            if (href.equals(_targetURI)) {
282                                    element = element.getParentElement();
283    
284                                    TextExtractor textExtractor = new TextExtractor(element);
285    
286                                    String body = textExtractor.toString();
287    
288                                    if (body.length() < PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH) {
289                                            element = element.getParentElement();
290    
291                                            if (element != null) {
292                                                    textExtractor = new TextExtractor(element);
293    
294                                                    body = textExtractor.toString();
295                                            }
296                                    }
297    
298                                    return StringUtil.shorten(
299                                            body, PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH);
300                            }
301                    }
302    
303                    return StringPool.BLANK;
304            }
305    
306            protected String getParam(Map<String, String[]> params, String name) {
307                    String[] paramArray = params.get(name);
308    
309                    if (paramArray == null) {
310                            String namespace = PortalUtil.getPortletNamespace(
311                                    PortletKeys.BLOGS);
312    
313                            paramArray = params.get(namespace + name);
314                    }
315    
316                    if (ArrayUtil.isNotEmpty(paramArray)) {
317                            return paramArray[0];
318                    }
319                    else {
320                            return null;
321                    }
322            }
323    
324            protected void validateSource() throws Exception {
325                    Source source = null;
326    
327                    try {
328                            String html = HttpUtil.URLtoString(_sourceURI);
329    
330                            source = new Source(html);
331                    }
332                    catch (Exception e) {
333                            if (_log.isDebugEnabled()) {
334                                    _log.debug(e, e);
335                            }
336    
337                            throw new UnavailableSourceURIException(
338                                    "Error accessing source URI", e);
339                    }
340    
341                    List<StartTag> startTags = source.getAllStartTags("a");
342    
343                    for (StartTag startTag : startTags) {
344                            String href = GetterUtil.getString(
345                                    startTag.getAttributeValue("href"));
346    
347                            if (href.equals(_targetURI)) {
348                                    return;
349                            }
350                    }
351    
352                    throw new InvalidSourceURIException(
353                            "Unable to find target URI in source");
354            }
355    
356            private static final Log _log = LogFactoryUtil.getLog(
357                    PingbackMethodImpl.class);
358    
359            private CommentManager _commentManager = BlogsUtil.getCommentManager();
360            private String _sourceURI;
361            private String _targetURI;
362    
363    }