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.portal.kernel.search;
016    
017    import com.liferay.portal.kernel.dao.search.SearchContainer;
018    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
019    import com.liferay.portal.kernel.portlet.PortletProvider;
020    import com.liferay.portal.kernel.portlet.PortletProviderUtil;
021    import com.liferay.portal.kernel.util.ClassUtil;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.HttpUtil;
024    import com.liferay.portal.kernel.util.PropsUtil;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.kernel.util.WebKeys;
029    import com.liferay.portal.kernel.uuid.PortalUUIDUtil;
030    import com.liferay.portal.kernel.xml.Document;
031    import com.liferay.portal.kernel.xml.Element;
032    import com.liferay.portal.kernel.xml.SAXReaderUtil;
033    import com.liferay.portal.model.Layout;
034    import com.liferay.portal.service.LayoutServiceUtil;
035    import com.liferay.portal.service.UserLocalServiceUtil;
036    import com.liferay.portal.theme.ThemeDisplay;
037    import com.liferay.portal.util.PortalUtil;
038    import com.liferay.portlet.PortletURLFactoryUtil;
039    
040    import java.util.Date;
041    
042    import javax.portlet.PortletMode;
043    import javax.portlet.PortletRequest;
044    import javax.portlet.PortletURL;
045    import javax.portlet.WindowState;
046    
047    import javax.servlet.http.HttpServletRequest;
048    
049    /**
050     * @author Charles May
051     * @author Brian Wing Shun Chan
052     */
053    public abstract class BaseOpenSearchImpl implements OpenSearch {
054    
055            public BaseOpenSearchImpl() {
056                    _enabled = GetterUtil.getBoolean(
057                            PropsUtil.get(getClass().getName()), true);
058    
059                    _openSearchURL = StringPool.BLANK;
060                    _openSearchDescriptionURL = StringPool.BLANK;
061            }
062    
063            public BaseOpenSearchImpl(
064                    String openSearchURL, String openSearchDescriptionURL) {
065    
066                    _openSearchURL = openSearchURL;
067                    _openSearchDescriptionURL = openSearchDescriptionURL;
068    
069                    _enabled = GetterUtil.getBoolean(
070                            PropsUtil.get(ClassUtil.getClassName(this)), true);
071            }
072    
073            @Override
074            public String getClassName() {
075                    return StringPool.BLANK;
076            }
077    
078            @Override
079            public boolean isEnabled() {
080                    return _enabled;
081            }
082    
083            @Override
084            public abstract String search(
085                            HttpServletRequest request, long groupId, long userId,
086                            String keywords, int startPage, int itemsPerPage, String format)
087                    throws SearchException;
088    
089            @Override
090            public String search(
091                            HttpServletRequest request, long userId, String keywords,
092                            int startPage, int itemsPerPage, String format)
093                    throws SearchException {
094    
095                    return search(
096                            request, 0, userId, keywords, startPage, itemsPerPage, format);
097            }
098    
099            @Override
100            public String search(HttpServletRequest request, String url)
101                    throws SearchException {
102    
103                    try {
104                            long userId = PortalUtil.getUserId(request);
105    
106                            if (userId == 0) {
107                                    long companyId = PortalUtil.getCompanyId(request);
108    
109                                    userId = UserLocalServiceUtil.getDefaultUserId(companyId);
110                            }
111    
112                            String keywords = GetterUtil.getString(
113                                    HttpUtil.getParameter(url, "keywords", false));
114                            int startPage = GetterUtil.getInteger(
115                                    HttpUtil.getParameter(url, "p", false), 1);
116                            int itemsPerPage = GetterUtil.getInteger(
117                                    HttpUtil.getParameter(url, "c", false),
118                                    SearchContainer.DEFAULT_DELTA);
119                            String format = GetterUtil.getString(
120                                    HttpUtil.getParameter(url, "format", false));
121    
122                            return search(
123                                    request, userId, keywords, startPage, itemsPerPage, format);
124                    }
125                    catch (SearchException se) {
126                            throw se;
127                    }
128                    catch (Exception e) {
129                            throw new SearchException(e);
130                    }
131            }
132    
133            protected void addSearchResult(
134                    Element root, long groupId, long scopeGroupId, String entryClassName,
135                    long entryClassPK, String title, String link, Date updated,
136                    String summary, double score, String format) {
137    
138                    addSearchResult(
139                            root, groupId, scopeGroupId, entryClassName, entryClassPK, title,
140                            link, updated, summary, new String[0], 0, score, format);
141            }
142    
143            protected void addSearchResult(
144                    Element root, long groupId, long scopeGroupId, String entryClassName,
145                    long entryClassPK, String title, String link, Date updated,
146                    String summary, String[] tags, double ratings, double score,
147                    String format) {
148    
149                    if (format.equals("rss")) {
150                            addSearchResultRSS(
151                                    root, groupId, scopeGroupId, entryClassName, entryClassPK,
152                                    title, link, updated, summary, tags, ratings, score);
153                    }
154                    else {
155                            addSearchResultAtom(
156                                    root, groupId, scopeGroupId, entryClassName, entryClassPK,
157                                    title, link, updated, summary, tags, ratings, score);
158                    }
159            }
160    
161            protected void addSearchResultAtom(
162                    Element root, long groupId, long scopeGroupId, String entryClassName,
163                    long entryClassPK, String title, String link, Date updated,
164                    String summary, String[] tags, double ratings, double score) {
165    
166                    // entry
167    
168                    Element entry = OpenSearchUtil.addElement(
169                            root, "entry", OpenSearchUtil.DEFAULT_NAMESPACE);
170    
171                    // groupId
172    
173                    OpenSearchUtil.addElement(
174                            entry, "groupId", OpenSearchUtil.LIFERAY_NAMESPACE, groupId);
175    
176                    // scopeGroupId
177    
178                    OpenSearchUtil.addElement(
179                            entry, "scopeGroupId", OpenSearchUtil.LIFERAY_NAMESPACE,
180                            scopeGroupId);
181    
182                    // entryClassName
183    
184                    OpenSearchUtil.addElement(
185                            entry, "entryClassName", OpenSearchUtil.LIFERAY_NAMESPACE,
186                            entryClassName);
187    
188                    // entryClassPK
189    
190                    OpenSearchUtil.addElement(
191                            entry, "entryClassPK", OpenSearchUtil.LIFERAY_NAMESPACE,
192                            entryClassPK);
193    
194                    // title
195    
196                    OpenSearchUtil.addElement(
197                            entry, "title", OpenSearchUtil.DEFAULT_NAMESPACE, title);
198    
199                    // link
200    
201                    Element entryLink = OpenSearchUtil.addElement(
202                            entry, "link", OpenSearchUtil.DEFAULT_NAMESPACE);
203    
204                    entryLink.addAttribute("href", link);
205    
206                    // id
207    
208                    OpenSearchUtil.addElement(
209                            entry, "id", OpenSearchUtil.DEFAULT_NAMESPACE,
210                            "urn:uuid:" + PortalUUIDUtil.generate());
211    
212                    // updated
213    
214                    OpenSearchUtil.addElement(
215                            entry, "updated", OpenSearchUtil.DEFAULT_NAMESPACE, updated);
216    
217                    // summary
218    
219                    OpenSearchUtil.addElement(
220                            entry, "summary", OpenSearchUtil.DEFAULT_NAMESPACE, summary);
221    
222                    // tags
223    
224                    OpenSearchUtil.addElement(
225                            entry, "tags", OpenSearchUtil.DEFAULT_NAMESPACE,
226                            StringUtil.merge(tags));
227    
228                    // ratings
229    
230                    OpenSearchUtil.addElement(
231                            entry, "ratings", OpenSearchUtil.DEFAULT_NAMESPACE, ratings);
232    
233                    // relevance:score
234    
235                    OpenSearchUtil.addElement(
236                            entry, "score", OpenSearchUtil.RELEVANCE_NAMESPACE, score);
237            }
238    
239            protected void addSearchResultRSS(
240                    Element root, long groupId, long scopeGroupId, String entryClassName,
241                    long entryClassPK, String title, String link, Date updated,
242                    String summary, String[] tags, double ratings, double score) {
243    
244                    // item
245    
246                    Element item = root.addElement("item");
247    
248                    // groupId
249    
250                    OpenSearchUtil.addElement(
251                            item, "groupId", OpenSearchUtil.LIFERAY_NAMESPACE, groupId);
252    
253                    // scopeGroupId
254    
255                    OpenSearchUtil.addElement(
256                            item, "scopeGroupId", OpenSearchUtil.LIFERAY_NAMESPACE,
257                            scopeGroupId);
258    
259                    // entryClassName
260    
261                    OpenSearchUtil.addElement(
262                            item, "entryClassName", OpenSearchUtil.LIFERAY_NAMESPACE,
263                            entryClassName);
264    
265                    // entryClassPK
266    
267                    OpenSearchUtil.addElement(
268                            item, "entryClassPK", OpenSearchUtil.LIFERAY_NAMESPACE,
269                            entryClassPK);
270    
271                    // title
272    
273                    OpenSearchUtil.addElement(
274                            item, "title", OpenSearchUtil.NO_NAMESPACE, title);
275    
276                    // link
277    
278                    OpenSearchUtil.addElement(
279                            item, "link", OpenSearchUtil.NO_NAMESPACE, link);
280    
281                    // summary
282    
283                    OpenSearchUtil.addElement(
284                            item, "description", OpenSearchUtil.NO_NAMESPACE, summary);
285    
286                    // tags
287    
288                    OpenSearchUtil.addElement(
289                            item, "tags", OpenSearchUtil.NO_NAMESPACE, StringUtil.merge(tags));
290    
291                    // ratings
292    
293                    OpenSearchUtil.addElement(
294                            item, "ratings", OpenSearchUtil.NO_NAMESPACE, ratings);
295    
296                    // relevance:score
297    
298                    OpenSearchUtil.addElement(
299                            item, "score", OpenSearchUtil.RELEVANCE_NAMESPACE, score);
300            }
301    
302            protected Object[] addSearchResults(
303                    String[] queryTerms, String keywords, int startPage, int itemsPerPage,
304                    int total, int start, String title, String searchPath, String format,
305                    ThemeDisplay themeDisplay) {
306    
307                    int totalPages = 0;
308    
309                    if ((total % itemsPerPage) == 0) {
310                            totalPages = total / itemsPerPage;
311                    }
312                    else {
313                            totalPages = (total / itemsPerPage) + 1;
314                    }
315    
316                    int previousPage = startPage - 1;
317                    int nextPage = startPage + 1;
318    
319                    Document doc = SAXReaderUtil.createDocument();
320    
321                    if (format.equals("rss")) {
322                            return addSearchResultsRSS(
323                                    doc, queryTerms, keywords, startPage, itemsPerPage, total,
324                                    start, totalPages, previousPage, nextPage, title, searchPath,
325                                    themeDisplay);
326                    }
327                    else {
328                            return addSearchResultsAtom(
329                                    doc, queryTerms, keywords, startPage, itemsPerPage, total,
330                                    start, totalPages, previousPage, nextPage, title, searchPath,
331                                    themeDisplay);
332                    }
333            }
334    
335            protected Object[] addSearchResultsAtom(
336                    Document doc, String[] queryTerms, String keywords, int startPage,
337                    int itemsPerPage, int total, int start, int totalPages,
338                    int previousPage, int nextPage, String title, String searchPath,
339                    ThemeDisplay themeDisplay) {
340    
341                    // feed
342    
343                    Element root = doc.addElement("feed");
344    
345                    root.add(OpenSearchUtil.getNamespace(OpenSearchUtil.DEFAULT_NAMESPACE));
346                    root.add(OpenSearchUtil.getNamespace(OpenSearchUtil.OS_NAMESPACE));
347                    root.add(
348                            OpenSearchUtil.getNamespace(OpenSearchUtil.RELEVANCE_NAMESPACE));
349    
350                    // title
351    
352                    OpenSearchUtil.addElement(
353                            root, "title", OpenSearchUtil.DEFAULT_NAMESPACE, title);
354    
355                    // updated
356    
357                    OpenSearchUtil.addElement(
358                            root, "updated", OpenSearchUtil.DEFAULT_NAMESPACE, new Date());
359    
360                    // author
361    
362                    Element author = OpenSearchUtil.addElement(
363                            root, "author", OpenSearchUtil.DEFAULT_NAMESPACE);
364    
365                    // name
366    
367                    OpenSearchUtil.addElement(
368                            author, "name", OpenSearchUtil.DEFAULT_NAMESPACE,
369                            themeDisplay.getUserId());
370    
371                    // id
372    
373                    OpenSearchUtil.addElement(
374                            root, "id", OpenSearchUtil.DEFAULT_NAMESPACE,
375                            "urn:uuid:" + PortalUUIDUtil.generate());
376    
377                    // queryTerms
378    
379                    OpenSearchUtil.addElement(
380                            root, "queryTerms", OpenSearchUtil.DEFAULT_NAMESPACE,
381                            StringUtil.merge(queryTerms, StringPool.COMMA_AND_SPACE));
382    
383                    // opensearch:totalResults
384    
385                    OpenSearchUtil.addElement(
386                            root, "totalResults", OpenSearchUtil.OS_NAMESPACE, total);
387    
388                    // opensearch:startIndex
389    
390                    OpenSearchUtil.addElement(
391                            root, "startIndex", OpenSearchUtil.OS_NAMESPACE, start + 1);
392    
393                    // opensearch:itemsPerPage
394    
395                    OpenSearchUtil.addElement(
396                            root, "itemsPerPage", OpenSearchUtil.OS_NAMESPACE, itemsPerPage);
397    
398                    // opensearch:Query
399    
400                    Element query = OpenSearchUtil.addElement(
401                            root, "Query", OpenSearchUtil.OS_NAMESPACE);
402    
403                    query.addAttribute("role", "request");
404                    query.addAttribute("searchTerms", keywords);
405                    query.addAttribute("startPage", String.valueOf(startPage));
406    
407                    // links
408    
409                    String searchURL = getOpenSearchURL(searchPath, themeDisplay);
410    
411                    OpenSearchUtil.addLink(
412                            root, searchURL, "self", keywords, startPage, itemsPerPage);
413                    OpenSearchUtil.addLink(
414                            root, searchURL, "first", keywords, 1, itemsPerPage);
415    
416                    if (previousPage > 0) {
417                            OpenSearchUtil.addLink(
418                                    root, searchURL, "previous", keywords, previousPage,
419                                    itemsPerPage);
420                    }
421    
422                    if (nextPage <= totalPages) {
423                            OpenSearchUtil.addLink(
424                                    root, searchURL, "next", keywords, nextPage, itemsPerPage);
425                    }
426    
427                    OpenSearchUtil.addLink(
428                            root, searchURL, "last", keywords, totalPages, itemsPerPage);
429    
430                    Element link = OpenSearchUtil.addElement(
431                            root, "link", OpenSearchUtil.DEFAULT_NAMESPACE);
432    
433                    link.addAttribute("rel", "search");
434                    link.addAttribute(
435                            "href", getOpenSearchDescriptionURL(searchPath, themeDisplay));
436                    link.addAttribute("type", "application/opensearchdescription+xml");
437    
438                    return new Object[] {doc, root};
439            }
440    
441            protected Object[] addSearchResultsRSS(
442                    Document doc, String[] queryTerms, String keywords, int startPage,
443                    int itemsPerPage, int total, int start, int totalPages,
444                    int previousPage, int nextPage, String title, String searchPath,
445                    ThemeDisplay themeDisplay) {
446    
447                    // rss
448    
449                    Element root = doc.addElement("rss");
450    
451                    root.addAttribute("version", "2.0");
452                    root.add(
453                            SAXReaderUtil.createNamespace(
454                                    "atom", "http://www.w3.org/2005/Atom"));
455                    root.add(OpenSearchUtil.getNamespace(OpenSearchUtil.OS_NAMESPACE));
456                    root.add(
457                            OpenSearchUtil.getNamespace(OpenSearchUtil.RELEVANCE_NAMESPACE));
458    
459                    // channel
460    
461                    Element channel = root.addElement("channel");
462    
463                    // title
464    
465                    OpenSearchUtil.addElement(
466                            channel, "title", OpenSearchUtil.NO_NAMESPACE, title);
467    
468                    // link
469    
470                    OpenSearchUtil.addElement(
471                            channel, "link", OpenSearchUtil.NO_NAMESPACE,
472                            getOpenSearchURL(searchPath, themeDisplay));
473    
474                    // description
475    
476                    OpenSearchUtil.addElement(
477                            channel, "description", OpenSearchUtil.NO_NAMESPACE, title);
478    
479                    // queryTerms
480    
481                    OpenSearchUtil.addElement(
482                            channel, "queryTerms", OpenSearchUtil.NO_NAMESPACE,
483                            StringUtil.merge(queryTerms, StringPool.COMMA_AND_SPACE));
484    
485                    // opensearch:totalResults
486    
487                    OpenSearchUtil.addElement(
488                            channel, "totalResults", OpenSearchUtil.OS_NAMESPACE, total);
489    
490                    // opensearch:startIndex
491    
492                    OpenSearchUtil.addElement(
493                            channel, "startIndex", OpenSearchUtil.OS_NAMESPACE, start + 1);
494    
495                    // opensearch:itemsPerPage
496    
497                    OpenSearchUtil.addElement(
498                            channel, "itemsPerPage", OpenSearchUtil.OS_NAMESPACE, itemsPerPage);
499    
500                    // opensearch:Query
501    
502                    Element query = OpenSearchUtil.addElement(
503                            channel, "Query", OpenSearchUtil.OS_NAMESPACE);
504    
505                    query.addAttribute("role", "request");
506                    query.addAttribute("searchTerms", keywords);
507                    query.addAttribute("startPage", String.valueOf(startPage));
508    
509                    return new Object[] {doc, channel};
510            }
511    
512            protected String getOpenSearchDescriptionURL(
513                    String searchPath, ThemeDisplay themeDisplay) {
514    
515                    if (Validator.isNotNull(_openSearchDescriptionURL)) {
516                            return _openSearchDescriptionURL;
517                    }
518    
519                    return themeDisplay.getPortalURL() + searchPath + "_description.xml";
520            }
521    
522            protected String getOpenSearchURL(
523                    String searchPath, ThemeDisplay themeDisplay) {
524    
525                    if (Validator.isNotNull(_openSearchURL)) {
526                            return _openSearchURL;
527                    }
528    
529                    return themeDisplay.getPortalURL() + searchPath;
530            }
531    
532            protected long getPlid(
533                            HttpServletRequest request, String portletId, long scopeGroupId)
534                    throws Exception {
535    
536                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
537    
538                    long layoutGroupId = scopeGroupId;
539    
540                    if (layout != null) {
541                            layoutGroupId = layout.getGroupId();
542                    }
543    
544                    long plid = LayoutServiceUtil.getDefaultPlid(
545                            layoutGroupId, scopeGroupId, false, portletId);
546    
547                    if (plid == 0) {
548                            plid = LayoutServiceUtil.getDefaultPlid(
549                                    layoutGroupId, scopeGroupId, true, portletId);
550                    }
551    
552                    if (plid == 0) {
553                            if (layout != null) {
554                                    plid = layout.getPlid();
555                            }
556                    }
557    
558                    return plid;
559            }
560    
561            protected PortletURL getPortletURL(
562                            HttpServletRequest request, String portletId)
563                    throws Exception {
564    
565                    return getPortletURL(request, portletId, 0);
566            }
567    
568            protected PortletURL getPortletURL(
569                            HttpServletRequest request, String portletId, long scopeGroupId)
570                    throws Exception {
571    
572                    long plid = getPlid(request, portletId, scopeGroupId);
573    
574                    PortletURL portletURL = PortletURLFactoryUtil.create(
575                            request, portletId, plid, PortletRequest.RENDER_PHASE);
576    
577                    portletURL.setPortletMode(PortletMode.VIEW);
578                    portletURL.setWindowState(WindowState.MAXIMIZED);
579    
580                    return portletURL;
581            }
582    
583            protected PortletURL getPortletURL(
584                            HttpServletRequest request, String className,
585                            PortletProvider.Action action, long scopeGroupId)
586                    throws Exception {
587    
588                    LiferayPortletURL portletURL =
589                            (LiferayPortletURL)PortletProviderUtil.getPortletURL(
590                                    request, className, action);
591    
592                    long plid = getPlid(request, portletURL.getPortletId(), scopeGroupId);
593    
594                    portletURL.setPlid(plid);
595    
596                    portletURL.setPortletMode(PortletMode.VIEW);
597                    portletURL.setWindowState(WindowState.MAXIMIZED);
598    
599                    return portletURL;
600            }
601    
602            private final boolean _enabled;
603            private final String _openSearchDescriptionURL;
604            private final String _openSearchURL;
605    
606    }