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