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.tools.searchindex;
029
030import org.opencms.configuration.CmsSearchConfiguration;
031import org.opencms.jsp.CmsJspActionElement;
032import org.opencms.main.CmsIllegalStateException;
033import org.opencms.main.OpenCms;
034import org.opencms.search.A_CmsSearchIndex;
035import org.opencms.search.CmsSearchIndex;
036import org.opencms.search.CmsSearchIndexSource;
037import org.opencms.search.CmsSearchManager;
038import org.opencms.search.I_CmsSearchIndex;
039import org.opencms.util.CmsStringUtil;
040import org.opencms.workplace.CmsWidgetDialog;
041import org.opencms.workplace.CmsWorkplaceSettings;
042
043import java.util.ArrayList;
044import java.util.HashMap;
045import java.util.List;
046import java.util.Locale;
047import java.util.Map;
048
049import javax.servlet.http.HttpServletRequest;
050import javax.servlet.http.HttpServletResponse;
051import javax.servlet.jsp.PageContext;
052
053/**
054 * Abstract dialog class for all dialogs that work on a <code>A_CmsSearchIndex</code>.<p>
055 *
056 * The <code>{@link #PARAM_INDEXNAME}</code> ("searchindex") is supported
057 * by means of widget technology (setter / getter).<p>
058 *
059 * Also - for accessing search functionality a member <code>{@link #m_searchManager}</code>
060 * is accessible for implementations. <p>
061 *
062 * @since 6.0.0
063 */
064public abstract class A_CmsEditSearchIndexDialog extends CmsWidgetDialog {
065
066    /** The name constraints when generating new index names. */
067    public static final String INDEX_NAME_CONSTRAINTS = "-._~$ ";
068
069    /** localized messages Keys prefix. */
070    public static final String KEY_PREFIX = "searchindex";
071
072    /** Defines which pages are valid for this dialog. */
073    public static final String[] PAGES = {"page1"};
074
075    /**
076     * The request parameter for the search index to work with when contacting
077     * this dialog from another. <p>
078     *
079     * It may be emtpy if we are on the new index dialog (/searchindex/new-index.jsp).<p>
080     *
081     **/
082    public static final String PARAM_INDEXNAME = "indexname";
083
084    /** The user object that is edited on this dialog. */
085    protected A_CmsSearchIndex m_index;
086
087    /** The search manager singleton for convenient access. **/
088    protected CmsSearchManager m_searchManager;
089
090    /** Stores the value of the request parameter for the search index Name. */
091    private String m_paramIndexName;
092
093    /**
094     * Public constructor with JSP action element.<p>
095     *
096     * @param jsp an initialized JSP action element
097     */
098    public A_CmsEditSearchIndexDialog(CmsJspActionElement jsp) {
099
100        super(jsp);
101    }
102
103    /**
104     * Public constructor with JSP variables.<p>
105     *
106     * @param context the JSP page context
107     * @param req the JSP request
108     * @param res the JSP response
109     */
110    public A_CmsEditSearchIndexDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) {
111
112        this(new CmsJspActionElement(context, req, res));
113    }
114
115    /**
116     * Writes the updated search configuration back to the XML
117     * configuration file and refreshes the complete list.<p>
118     */
119    protected static void writeConfiguration() {
120
121        // update the XML configuration
122        OpenCms.writeConfiguration(CmsSearchConfiguration.class);
123    }
124
125    /**
126     * Commits the edited search index to the search manager.<p>
127     */
128    @Override
129    public void actionCommit() {
130
131        List<Throwable> errors = new ArrayList<Throwable>();
132
133        try {
134
135            // if new create it first
136            if (!m_searchManager.getSearchIndexesAll().contains(m_index)) {
137                // check the index name for invalid characters
138                CmsStringUtil.checkName(
139                    m_index.getName(),
140                    INDEX_NAME_CONSTRAINTS,
141                    Messages.ERR_SEARCHINDEX_BAD_INDEXNAME_4,
142                    Messages.get());
143                // empty or null name and uniqueness check in add method
144                m_searchManager.addSearchIndex(m_index);
145            }
146            // check if field configuration has been updated, if thus set field configuration to the now used
147            if (!m_index.getFieldConfigurationName().equals(m_index.getFieldConfiguration().getName())) {
148                m_index.setFieldConfiguration(
149                    m_searchManager.getFieldConfiguration(m_index.getFieldConfigurationName()));
150            }
151            writeConfiguration();
152
153        } catch (Throwable t) {
154            errors.add(t);
155        }
156
157        // set the list of errors to display when saving failed
158        setCommitErrors(errors);
159    }
160
161    /**
162     * Returns the request parameter value for parameter paramSearchIndex. <p>
163     *
164     * @return the request parameter value for parameter paramSearchIndex
165     */
166    public String getParamIndexName() {
167
168        return m_paramIndexName;
169    }
170
171    /**
172     * Sets the value of the request parameter paramSearchIndex.
173     *
174     * @param paramSearchIndex the value of the request parameter paramSearchIndex to set
175     */
176    public void setParamIndexName(String paramSearchIndex) {
177
178        m_paramIndexName = paramSearchIndex;
179    }
180
181    /**
182     * Initializes the user object (a <code>{@link A_CmsSearchIndex}</code> instance.<p>
183     *
184     * Implementation always have to call <code>"super.defineWidgets()"</code> first as
185     * this action may only be done here (relies on filled request parameters, the next
186     * following operation <code>{@link CmsWidgetDialog#createDialogHtml()}</code> will
187     * rely on this. <p>
188     *
189     * @see org.opencms.workplace.CmsWidgetDialog#defineWidgets()
190     */
191    @Override
192    protected void defineWidgets() {
193
194        initUserObject();
195        setKeyPrefix(KEY_PREFIX);
196
197    }
198
199    /**
200     * @see org.opencms.workplace.CmsWidgetDialog#getPageArray()
201     */
202    @Override
203    protected String[] getPageArray() {
204
205        return PAGES;
206    }
207
208    /**
209     * Returns the search index.<p>
210     *
211     * @return the search index
212     */
213    protected A_CmsSearchIndex getSearchIndexIndex() {
214
215        return m_index;
216    }
217
218    /**
219     * Returns the root path of this dialog (path relative to "/system/workplace/admin").<p>
220     *
221     * @return the root path of this dialog (path relative to "/system/workplace/admin")
222     */
223    protected String getToolPath() {
224
225        return "/searchindex";
226    }
227
228    /**
229     * @see org.opencms.workplace.CmsWorkplace#initMessages()
230     */
231    @Override
232    protected void initMessages() {
233
234        // add specific dialog resource bundle
235        addMessages(Messages.get().getBundleName());
236        // add default resource bundles
237        super.initMessages();
238    }
239
240    /**
241     * Initializes the user object to work with depending on the dialog state and request parameters.<p>
242     *
243     * Two initializations of the user object on first dialog call are possible:
244     * <ul>
245     * <li>edit an existing search index</li>
246     * <li>create a new search index with default initialization</li>
247     * </ul>
248     */
249    protected void initUserObject() {
250
251        try {
252            I_CmsSearchIndex idx = m_searchManager.getIndex(getParamIndexName());
253            m_index = idx instanceof CmsSearchIndex ? (CmsSearchIndex)idx : null;
254            if (m_index == null) {
255                m_index = createDummySearchIndex();
256            }
257        } catch (Exception e) {
258            m_index = createDummySearchIndex();
259        }
260    }
261
262    /**
263     * Overridden to initialize the internal <code>CmsSearchManager</code> before initWorkplaceRequestValues ->
264     * defineWidgets ->  will access it (NPE). <p>
265     *
266     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceMembers(org.opencms.jsp.CmsJspActionElement)
267     */
268    @Override
269    protected void initWorkplaceMembers(CmsJspActionElement jsp) {
270
271        m_searchManager = OpenCms.getSearchManager();
272        super.initWorkplaceMembers(jsp);
273    }
274
275    /**
276     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
277     */
278    @Override
279    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
280
281        // initialize parameters and dialog actions in super implementation
282        super.initWorkplaceRequestValues(settings, request);
283
284        // save the current search index
285        @SuppressWarnings("unchecked")
286        Map<String, A_CmsSearchIndex> dialogObject = (Map<String, A_CmsSearchIndex>)getDialogObject();
287        if (dialogObject == null) {
288            dialogObject = new HashMap<String, A_CmsSearchIndex>();
289            dialogObject.put(PARAM_INDEXNAME, m_index);
290            setDialogObject(dialogObject);
291        }
292
293    }
294
295    /**
296     * Checks if the new search index dialog has to be displayed.<p>
297     *
298     * @return <code>true</code> if the new search index dialog has to be displayed
299     */
300    protected boolean isNewSearchIndex() {
301
302        return DIALOG_INITIAL.equals(getParamAction());
303    }
304
305    /**
306     * @see org.opencms.workplace.CmsWidgetDialog#validateParamaters()
307     */
308    @Override
309    protected void validateParamaters() throws Exception {
310
311        if (!isNewSearchIndex()) {
312            // test the needed parameters: if initial we have "indexname", if from same widget we have name.0
313            if ((getParamIndexName() == null) && (getJsp().getRequest().getParameter("name.0") == null)) {
314                throw new CmsIllegalStateException(
315                    Messages.get().container(Messages.ERR_SEARCHINDEX_EDIT_MISSING_PARAM_1, PARAM_INDEXNAME));
316            }
317        }
318    }
319
320    /**
321     * Creates a dummy index source.<p>
322     *
323     * @return the dummy search index source
324     */
325    private CmsSearchIndexSource createDummyIndexSource() {
326
327        CmsSearchIndexSource result = new CmsSearchIndexSource();
328        result.setName("default");
329        result.setIndexerClassName("org.opencms.search.CmsVfsIndexer");
330        result.addDocumentType("html");
331        result.addDocumentType("generic");
332        result.addDocumentType("pdf");
333        // add search index source to config:
334        m_searchManager.addSearchIndexSource(result);
335        return result;
336    }
337
338    /**
339     * Creates a "dummy" search index that is not linked to the search manager and has
340     * a <code>null</code> name property that will be used for being filled with
341     * the widget bean technology. <p>
342     *
343     * @return a "dummy" search index that is not linked to the search manager and has
344     *         a <code>null</code> name property that will be used for being filled with
345     *         the widget bean technology
346     */
347    private CmsSearchIndex createDummySearchIndex() {
348
349        CmsSearchIndex result = new CmsSearchIndex();
350        result.setLocale(Locale.ENGLISH);
351        result.setProject("Online");
352        result.setRebuildMode("auto");
353
354        // find default source
355        Map<String, CmsSearchIndexSource> sources = m_searchManager.getSearchIndexSources();
356        if (sources.isEmpty()) {
357            CmsSearchIndexSource source = createDummyIndexSource();
358            sources.put(source.getName(), source);
359        }
360        result.addSourceName(sources.keySet().iterator().next());
361
362        return result;
363    }
364}