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