001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.list;
029
030import org.opencms.i18n.CmsEncoder;
031import org.opencms.i18n.CmsMessageContainer;
032import org.opencms.util.CmsStringUtil;
033import org.opencms.workplace.CmsWorkplace;
034
035import java.util.ArrayList;
036import java.util.Collections;
037import java.util.Iterator;
038import java.util.List;
039
040/**
041 * Default implementation for a search action in an html list.<p>
042 *
043 * It allows to search in several columns, including item details.<p>
044 *
045 * @since 6.0.0
046 */
047public class CmsListSearchAction extends A_CmsListSearchAction {
048
049    /** the html id for the input element of the search bar. */
050    public static final String SEARCH_BAR_INPUT_ID = "listSearchFilter";
051
052    /** Signals whether the search is case sensitive or not. */
053    private boolean m_caseInSensitive;
054
055    /** Ids of Columns to search into. */
056    private final List<CmsListColumnDefinition> m_columns = new ArrayList<CmsListColumnDefinition>();
057
058    /**
059     * Default Constructor.<p>
060     *
061     * @param column the column to search into
062     */
063    public CmsListSearchAction(CmsListColumnDefinition column) {
064
065        super();
066        useDefaultShowAllAction();
067        m_columns.add(column);
068    }
069
070    /**
071     * Adds a column to search into.<p>
072     *
073     * @param column the additional column to search into
074     */
075    public void addColumn(CmsListColumnDefinition column) {
076
077        m_columns.add(column);
078    }
079
080    /**
081     * Returns the html code for the search bar.<p>
082     *
083     * @param wp the workplace context
084     *
085     * @return html code
086     */
087    public String barHtml(CmsWorkplace wp) {
088
089        if (wp == null) {
090            wp = getWp();
091        }
092        StringBuffer html = new StringBuffer(1024);
093        html.append("\t\t<input type='text' name='");
094        html.append(SEARCH_BAR_INPUT_ID);
095        html.append("' id='");
096        html.append(SEARCH_BAR_INPUT_ID);
097        html.append("' value='");
098        if (wp instanceof A_CmsListDialog) {
099            // http://www.securityfocus.com/archive/1/490498: searchfilter cross site scripting vulnerability:
100            html.append(
101                CmsStringUtil.escapeJavaScript(
102                    CmsEncoder.escapeXml(((A_CmsListDialog)wp).getList().getSearchFilter())));
103        }
104        html.append("' size='20' maxlength='245' style='vertical-align: bottom;' >\n");
105        html.append(buttonHtml(wp));
106        if (getShowAllAction() != null) {
107            html.append("&nbsp;&nbsp;");
108            html.append(getShowAllAction().buttonHtml());
109        }
110        return html.toString();
111    }
112
113    /**
114     * @see org.opencms.workplace.list.A_CmsListSearchAction#buttonHtml(org.opencms.workplace.CmsWorkplace)
115     */
116    @Override
117    public String buttonHtml(CmsWorkplace wp) {
118
119        // delay the composition of the help text as much as possible
120        if (getHelpText() == EMPTY_MESSAGE) {
121            String columns = "";
122            Iterator<CmsListColumnDefinition> it = m_columns.iterator();
123            while (it.hasNext()) {
124                CmsListColumnDefinition col = it.next();
125                columns += "${key." + col.getName().getKey() + "}";
126                if (it.hasNext()) {
127                    columns += ", ";
128                }
129            }
130            if (columns.lastIndexOf(", ") > 0) {
131                columns = columns.substring(0, columns.lastIndexOf(", "))
132                    + " and "
133                    + columns.substring(columns.lastIndexOf(", ") + 2);
134            }
135            setHelpText(
136                new CmsMessageContainer(
137                    Messages.get(),
138                    Messages.GUI_LIST_ACTION_SEARCH_HELP_1,
139                    new Object[] {columns}));
140        }
141        return super.buttonHtml(wp);
142    }
143
144    /**
145     * Returns a sublist of the given items, that match the given filter string.<p>
146     *
147     * @param items the items to filter
148     * @param filter the string to filter
149     *
150     * @return the filtered sublist
151     */
152    public List<CmsListItem> filter(List<CmsListItem> items, String filter) {
153
154        if (CmsStringUtil.isEmptyOrWhitespaceOnly(filter)) {
155            return items;
156        }
157        String filterCriteria = filter;
158        if (m_caseInSensitive) {
159            filterCriteria = filter.toLowerCase();
160        }
161
162        List<CmsListItem> res = new ArrayList<CmsListItem>();
163        Iterator<CmsListItem> itItems = items.iterator();
164        while (itItems.hasNext()) {
165            CmsListItem item = itItems.next();
166            if (res.contains(item)) {
167                continue;
168            }
169            Iterator<CmsListColumnDefinition> itCols = m_columns.iterator();
170            while (itCols.hasNext()) {
171                CmsListColumnDefinition col = itCols.next();
172                if (item.get(col.getId()) == null) {
173                    continue;
174                }
175                String columnValue = item.get(col.getId()).toString();
176                if (m_caseInSensitive) {
177                    columnValue = columnValue.toLowerCase();
178                }
179                if (columnValue.indexOf(filterCriteria) > -1) {
180                    res.add(item);
181                    break;
182                }
183            }
184        }
185        return res;
186    }
187
188    /**
189     * Returns the list of columns to be searched.<p>
190     *
191     * @return a list of {@link CmsListColumnDefinition} objects
192     */
193    public List<CmsListColumnDefinition> getColumns() {
194
195        return Collections.unmodifiableList(m_columns);
196    }
197
198    /**
199     * Returns the caseInSensitive.<p>
200     *
201     * @return the caseInSensitive
202     */
203    public boolean isCaseInSensitive() {
204
205        return m_caseInSensitive;
206    }
207
208    /**
209     * Sets the caseInSensitive.<p>
210     *
211     * @param caseInSensitive the caseInSensitive to set
212     */
213    public void setCaseInSensitive(boolean caseInSensitive) {
214
215        m_caseInSensitive = caseInSensitive;
216    }
217
218    /**
219     * Sets the current search filter.<p>
220     *
221     * @param filter the current search filter
222     *
223     * @deprecated use {@link CmsHtmlList#setSearchFilter(String)} instead
224     */
225    @Deprecated
226    public void setSearchFilter(String filter) {
227
228        // empty
229    }
230
231    /**
232     * @see org.opencms.workplace.list.I_CmsListAction#setWp(org.opencms.workplace.list.A_CmsListDialog)
233     */
234    @Override
235    public void setWp(A_CmsListDialog wp) {
236
237        super.setWp(wp);
238        if (getShowAllAction() != null) {
239            getShowAllAction().setWp(wp);
240        }
241    }
242}