001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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.database;
029
030import org.opencms.configuration.CmsImportExportConfiguration;
031import org.opencms.i18n.CmsEncoder;
032import org.opencms.importexport.CmsExtendedHtmlImportDefault;
033import org.opencms.jsp.CmsJspActionElement;
034import org.opencms.main.CmsException;
035import org.opencms.main.OpenCms;
036import org.opencms.util.CmsRequestUtil;
037import org.opencms.util.CmsStringUtil;
038import org.opencms.widgets.CmsCheckboxWidget;
039import org.opencms.widgets.CmsHttpUploadWidget;
040import org.opencms.widgets.CmsInputWidget;
041import org.opencms.widgets.CmsSelectWidget;
042import org.opencms.widgets.CmsSelectWidgetOption;
043import org.opencms.widgets.CmsVfsFileWidget;
044import org.opencms.widgets.I_CmsWidget;
045import org.opencms.workplace.CmsWidgetDialog;
046import org.opencms.workplace.CmsWidgetDialogParameter;
047import org.opencms.workplace.explorer.CmsNewResourceXmlPage;
048import org.opencms.workplace.tools.CmsToolDialog;
049import org.opencms.workplace.tools.CmsToolManager;
050
051import java.io.File;
052import java.io.FileOutputStream;
053import java.util.ArrayList;
054import java.util.HashMap;
055import java.util.Iterator;
056import java.util.List;
057import java.util.Locale;
058import java.util.Map;
059import java.util.TreeMap;
060
061import org.apache.commons.fileupload2.core.DiskFileItem;
062
063import jakarta.servlet.http.HttpServletRequest;
064import jakarta.servlet.http.HttpServletResponse;
065import jakarta.servlet.jsp.PageContext;
066
067/**
068 * Dialog to define an extended HTML import in the administration view.<p>
069 *
070 * WARNING: If the zip file is to great to upload, then only a log entry
071 * is created from the following method and this dialog is only refreshed:<p>
072 * {@link org.opencms.util.CmsRequestUtil#readMultipartFileItems(HttpServletRequest)}. <p>
073 *
074 * There are three modes to show the dialog:<p>
075 *
076 * <ul>
077 *  <li>{@link #MODE_DEFAULT}
078 *      <ul>
079 *          <li>HTTP-Upload is not shown.</li>
080 *          <li>default values are saved by action commit.</li>
081 *      </ul>
082 *  </li>
083 *  <li>{@link #MODE_STANDARD}
084 *      <ul>
085 *          <li>HTTP-Upload is shown.</li>
086 *          <li>the HTML files would be imported by action commit.</li>
087 *      </ul>
088 *  </li>
089 *  <li>{@link #MODE_ADVANCED}
090 *      <ul>
091 *          <li>This dialog is needed for the advanced button in the new Dialog for the user.</li>
092 *          <li>HTTP-Upload is shown.</li>
093 *          <li>DestinationDir is not shown.</li>
094 *          <li>InputDir is not shown.</li>
095 *          <li>the HTML files would be imported by action commit.</li>
096 *      </ul>
097 *  </li>
098 * </ul>
099 *
100 */
101public class CmsHtmlImportDialog extends CmsWidgetDialog {
102
103    /** the JSP path, which requested the default mode. */
104    public static final String IMPORT_DEFAULT_PATH = "htmldefault.jsp";
105
106    /** the JSP path, which requested the standard mode. */
107    public static final String IMPORT_STANDARD_PATH = "htmlimport.jsp";
108
109    /** localized messages Keys prefix. */
110    public static final String KEY_PREFIX = "htmlimport";
111
112    /** shows this dialog in the advanced mode.*/
113    public static final String MODE_ADVANCED = "advanced";
114
115    /** shows this dialog in the default mode.*/
116    public static final String MODE_DEFAULT = "default";
117
118    /** shows this dialog in the standard mode.*/
119    public static final String MODE_STANDARD = "standard";
120
121    /** Defines which pages are valid for this dialog. */
122    public static final String[] PAGES = {"page1"};
123
124    /** The import JSP report workplace URI. */
125    protected static final String IMPORT_ACTION_REPORT = PATH_WORKPLACE + "admin/database/reports/htmlimport.jsp";
126
127    /** The HTML import object that is edited on this dialog. */
128    protected CmsHtmlImport m_htmlimport;
129
130    /**the current mode of the dialog. */
131    private String m_dialogMode;
132
133    /**
134     * Public constructor with JSP action element.<p>
135     *
136     * @param jsp an initialized JSP action element
137     */
138    public CmsHtmlImportDialog(CmsJspActionElement jsp) {
139
140        super(jsp);
141    }
142
143    /**
144     * Public constructor with JSP variables.<p>
145     *
146     * @param context the JSP page context
147     * @param req the JSP request
148     * @param res the JSP response
149     */
150    public CmsHtmlImportDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) {
151
152        this(new CmsJspActionElement(context, req, res));
153    }
154
155    /**
156     * @see org.opencms.workplace.CmsWidgetDialog#actionCommit()
157     */
158    @Override
159    public void actionCommit() {
160
161        List errors = new ArrayList();
162        setDialogObject(m_htmlimport);
163
164        try {
165
166            if (isDisplayMode(MODE_DEFAULT)) {
167                // default mode the default values are saved in the configuration file
168
169                m_htmlimport.validate(null, true);
170
171                // fill the extended
172                fillExtendedHtmlImportDefault();
173
174                // save the default values in the file
175                OpenCms.writeConfiguration(CmsImportExportConfiguration.class);
176
177            } else {
178                // advanced and standard mode the importing is starting
179                DiskFileItem fi = getHttpImportFileItem();
180
181                m_htmlimport.validate(fi, false);
182
183                // write the file in the temporary directory
184                writeHttpImportDir(fi);
185
186                Map params = new HashMap();
187
188                // set the name of this class to get dialog object in report
189                params.put(CmsHtmlImportReport.PARAM_CLASSNAME, this.getClass().getName());
190
191                // set style to display report in correct layout
192                params.put(PARAM_STYLE, CmsToolDialog.STYLE_NEW);
193
194                // set close link to get back to overview after finishing the import
195                params.put(PARAM_CLOSELINK, CmsToolManager.linkForToolPath(getJsp(), "/database"));
196
197                // redirect to the report output JSP
198                getToolManager().jspForwardPage(this, IMPORT_ACTION_REPORT, params);
199            }
200        } catch (Throwable t) {
201            errors.add(t);
202        }
203
204        // set the list of errors to display when saving failed
205        setCommitErrors(errors);
206    }
207
208    /**
209     * @see org.opencms.workplace.CmsWidgetDialog#createDialogHtml(java.lang.String)
210     */
211    @Override
212    protected String createDialogHtml(String dialog) {
213
214        StringBuffer result = new StringBuffer(1024);
215
216        result.append(createWidgetTableStart());
217        // show error header once if there were validation errors
218        result.append(createWidgetErrorHeader());
219
220        if (dialog.equals(PAGES[0])) {
221
222            // create the widgets for the first dialog page
223            int row = (isDisplayMode(MODE_DEFAULT) ? 1 : (isDisplayMode(MODE_ADVANCED) ? 0 : 2));
224            result.append(createWidgetBlockStart(key(Messages.GUI_HTMLIMPORT_BLOCK_LABEL_FOLDER_0)));
225            result.append(createDialogRowsHtml(0, row));
226            result.append(createWidgetBlockEnd());
227            row++;
228
229            result.append(createWidgetBlockStart(key(Messages.GUI_HTMLIMPORT_BLOCK_LABEL_GALLERY_0)));
230            result.append(createDialogRowsHtml(row, row + 2));
231            result.append(createWidgetBlockEnd());
232
233            result.append(createWidgetBlockStart(key(Messages.GUI_HTMLIMPORT_BLOCK_LABEL_SETTINGS_0)));
234
235            result.append(createDialogRowsHtml(row + 3, row + 10));
236            result.append(createWidgetBlockEnd());
237        }
238
239        result.append(createWidgetTableEnd());
240        return result.toString();
241    }
242
243    /**
244     * This must be overwrite, because we need additional the 'enctype' parameter.<p>
245     *
246     * @see org.opencms.workplace.CmsWidgetDialog#defaultActionHtmlContent()
247     */
248    @Override
249    protected String defaultActionHtmlContent() {
250
251        StringBuffer result = new StringBuffer(2048);
252        result.append("<form name=\"EDITOR\" id=\"EDITOR\" method=\"post\" action=\"").append(getDialogRealUri());
253        result.append("\" class=\"nomargin\" onsubmit=\"return submitAction('").append(DIALOG_OK).append(
254            "', null, 'EDITOR');\" enctype=\"multipart/form-data\">\n");
255        result.append(dialogContentStart(getDialogTitle()));
256        result.append(buildDialogForm());
257        result.append(dialogContentEnd());
258        result.append(dialogButtonsCustom());
259        result.append(paramsAsHidden());
260        if (getParamFramename() == null) {
261            result.append("\n<input type=\"hidden\" name=\"").append(PARAM_FRAMENAME).append("\" value=\"\">\n");
262        }
263        result.append("</form>\n");
264        result.append(getWidgetHtmlEnd());
265        return result.toString();
266    }
267
268    /**
269     * @see org.opencms.workplace.CmsWidgetDialog#defineWidgets()
270     */
271    @Override
272    protected void defineWidgets() {
273
274        initHtmlImportObject();
275        setKeyPrefix(KEY_PREFIX);
276
277        if (!isDisplayMode(MODE_ADVANCED)) {
278            addWidget(getDialogParameter("inputDir", new CmsInputWidget()));
279        }
280        if (!isDisplayMode(MODE_DEFAULT)) {
281            addWidget(getDialogParameter("httpDir", new CmsHttpUploadWidget()));
282        }
283        if (!isDisplayMode(MODE_ADVANCED)) {
284            addWidget(
285                getDialogParameter(
286                    "destinationDir",
287                    new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
288        }
289
290        addWidget(
291            getDialogParameter(
292                "imageGallery",
293                new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
294        addWidget(
295            getDialogParameter(
296                "downloadGallery",
297                new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
298        addWidget(
299            getDialogParameter("linkGallery", new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
300
301        addWidget(getDialogParameter("template", new CmsSelectWidget(getTemplates())));
302        addWidget(getDialogParameter("element", new CmsInputWidget()));
303        addWidget(getDialogParameter("locale", new CmsSelectWidget(getLocales())));
304        addWidget(getDialogParameter("inputEncoding", new CmsInputWidget()));
305        addWidget(getDialogParameter("startPattern", new CmsInputWidget()));
306        addWidget(getDialogParameter("endPattern", new CmsInputWidget()));
307
308        addWidget(getDialogParameter("overwrite", new CmsCheckboxWidget()));
309        addWidget(getDialogParameter("keepBrokenLinks", new CmsCheckboxWidget()));
310    }
311
312    /**
313     * This function fills the <code> {@link CmsHtmlImport} </code> Object based on
314     * the values in the import/export configuration file. <p>
315     */
316    protected void fillHtmlImport() {
317
318        CmsExtendedHtmlImportDefault extimport = OpenCms.getImportExportManager().getExtendedHtmlImportDefault();
319        m_htmlimport.setDestinationDir(extimport.getDestinationDir());
320        m_htmlimport.setInputDir(extimport.getInputDir());
321        m_htmlimport.setDownloadGallery(extimport.getDownloadGallery());
322        m_htmlimport.setImageGallery(extimport.getImageGallery());
323        m_htmlimport.setLinkGallery(extimport.getLinkGallery());
324        m_htmlimport.setTemplate(extimport.getTemplate());
325        m_htmlimport.setElement(extimport.getElement());
326        m_htmlimport.setLocale(extimport.getLocale());
327        m_htmlimport.setInputEncoding(extimport.getEncoding());
328        m_htmlimport.setStartPattern(extimport.getStartPattern());
329        m_htmlimport.setEndPattern(extimport.getEndPattern());
330        m_htmlimport.setOverwrite(Boolean.valueOf(extimport.getOverwrite()).booleanValue());
331        m_htmlimport.setKeepBrokenLinks(Boolean.valueOf(extimport.getKeepBrokenLinks()).booleanValue());
332    }
333
334    /**
335     * @see org.opencms.workplace.CmsWidgetDialog#fillWidgetValues(jakarta.servlet.http.HttpServletRequest)
336     */
337    @Override
338    protected void fillWidgetValues(HttpServletRequest request) {
339
340        Map parameters;
341        if (getMultiPartFileItems() != null) {
342            parameters = CmsRequestUtil.readParameterMapFromMultiPart(
343                getCms().getRequestContext().getEncoding(),
344                getMultiPartFileItems());
345        } else {
346            parameters = request.getParameterMap();
347        }
348        Map processedParameters = new HashMap();
349        Iterator p = parameters.entrySet().iterator();
350        // make sure all "hidden" widget parameters are decoded
351        while (p.hasNext()) {
352            Map.Entry entry = (Map.Entry)p.next();
353            String key = (String)entry.getKey();
354            String[] values = (String[])entry.getValue();
355            if (key.startsWith(HIDDEN_PARAM_PREFIX)) {
356                // this is an encoded hidden parameter
357                key = key.substring(HIDDEN_PARAM_PREFIX.length());
358                String[] newValues = new String[values.length];
359                for (int l = 0; l < values.length; l++) {
360                    newValues[l] = CmsEncoder.decode(values[l], getCms().getRequestContext().getEncoding());
361                }
362                values = newValues;
363            }
364            processedParameters.put(key, values);
365        }
366
367        // now process the parameters
368        m_widgetParamValues = new HashMap();
369        Iterator i = getWidgets().iterator();
370
371        while (i.hasNext()) {
372            // check for all widget base parameters
373            CmsWidgetDialogParameter base = (CmsWidgetDialogParameter)i.next();
374
375            List params = new ArrayList();
376            int maxOccurs = base.getMaxOccurs();
377
378            boolean onPage = false;
379            if (base.isCollectionBase()) {
380                // for a collection base, check if we are on the page where the collection base is shown
381                if (CmsStringUtil.isNotEmpty(getParamAction()) && !DIALOG_INITIAL.equals(getParamAction())) {
382                    // if no action set (usually for first display of dialog) make sure all values are shown
383                    // DIALOG_INITIAL is a special value for the first display and must be handled the same way
384                    String page = getParamPage();
385                    // keep in mind that since the paramPage will be set AFTER the widget values are filled,
386                    // so the first time this page is called from another page the following will result to "false",
387                    // but for every "submit" on the page this will be "true"
388                    onPage = CmsStringUtil.isEmpty(page)
389                        || CmsStringUtil.isEmpty(base.getDialogPage())
390                        || base.getDialogPage().equals(page);
391                }
392            }
393
394            for (int j = 0; j < maxOccurs; j++) {
395                // check for all possible values in the request parameters
396                String id = CmsWidgetDialogParameter.createId(base.getName(), j);
397
398                boolean required = (params.size() < base.getMinOccurs())
399                    || (processedParameters.get(id) != null)
400                    || (!onPage && base.hasValue(j));
401
402                if (required) {
403                    CmsWidgetDialogParameter param = new CmsWidgetDialogParameter(base, params.size(), j);
404                    param.setKeyPrefix(KEY_PREFIX);
405                    base.getWidget().setEditorValue(getCms(), processedParameters, this, param);
406                    params.add(param);
407                }
408            }
409            m_widgetParamValues.put(base.getName(), params);
410        }
411    }
412
413    /**
414     * This function creates a <code> {@link CmsWidgetDialogParameter} </code> Object based
415     * on the given properties.<p>
416     *
417     * @param property the base object property to map the parameter to / from
418     * @param widget the widget used for this dialog-parameter
419     *
420     * @return a <code> {@link CmsWidgetDialogParameter} </code> Object
421     */
422    protected CmsWidgetDialogParameter getDialogParameter(String property, I_CmsWidget widget) {
423
424        return new CmsWidgetDialogParameter(m_htmlimport, property, PAGES[0], widget);
425    }
426
427    /**
428     * @see org.opencms.workplace.CmsWidgetDialog#getPageArray()
429     */
430    @Override
431    protected String[] getPageArray() {
432
433        return PAGES;
434    }
435
436    /**
437     * Initializes this widget dialog's object.<p>
438     */
439    protected void initHtmlImportObject() {
440
441        Object o;
442        String uri = getJsp().getRequestContext().getUri();
443        if ((uri == null) || uri.endsWith(IMPORT_STANDARD_PATH)) {
444            m_dialogMode = MODE_STANDARD;
445        } else if (uri.endsWith(IMPORT_DEFAULT_PATH)) {
446            m_dialogMode = MODE_DEFAULT;
447        } else {
448            m_dialogMode = MODE_ADVANCED;
449        }
450        if (CmsStringUtil.isEmpty(getParamAction())) {
451            o = new CmsHtmlImport(getJsp().getCmsObject());
452        } else {
453            // this is not the initial call, get the job object from session
454            o = getDialogObject();
455        }
456
457        if (!(o instanceof CmsHtmlImport)) {
458            // create a new HTML import handler object
459            m_htmlimport = new CmsHtmlImport(getJsp().getCmsObject());
460        } else {
461            // reuse HTML import handler object stored in session
462            m_htmlimport = (CmsHtmlImport)o;
463            // this is needed, because the user can switch between the sites, now get the current
464            m_htmlimport.setCmsObject(getJsp().getCmsObject());
465        }
466
467        // gets the data from the configuration file
468        fillHtmlImport();
469    }
470
471    /**
472     * @see org.opencms.workplace.CmsWorkplace#initMessages()
473     */
474    @Override
475    protected void initMessages() {
476
477        // add specific dialog resource bundle
478        addMessages(Messages.get().getBundleName());
479        // add default resource bundles
480        super.initMessages();
481    }
482
483    /**
484     * This function fills the <code> {@link CmsExtendedHtmlImportDefault} </code> Object based on
485     * the current values in the dialog. <p>
486     */
487    private void fillExtendedHtmlImportDefault() {
488
489        CmsExtendedHtmlImportDefault extimport = OpenCms.getImportExportManager().getExtendedHtmlImportDefault();
490        extimport.setDestinationDir(m_htmlimport.getDestinationDir());
491        extimport.setInputDir(m_htmlimport.getInputDir());
492        extimport.setDownloadGallery(m_htmlimport.getDownloadGallery());
493        extimport.setImageGallery(m_htmlimport.getImageGallery());
494        extimport.setLinkGallery(m_htmlimport.getLinkGallery());
495        extimport.setTemplate(m_htmlimport.getTemplate());
496        extimport.setElement(m_htmlimport.getElement());
497        extimport.setLocale(m_htmlimport.getLocale());
498        extimport.setEncoding(m_htmlimport.getInputEncoding());
499        extimport.setStartPattern(m_htmlimport.getStartPattern());
500        extimport.setEndPattern(m_htmlimport.getEndPattern());
501        extimport.setOverwrite(Boolean.toString(m_htmlimport.isOverwrite()));
502        extimport.setKeepBrokenLinks(Boolean.toString(m_htmlimport.isKeepBrokenLinks()));
503        OpenCms.getImportExportManager().setExtendedHtmlImportDefault(extimport);
504    }
505
506    /**
507     * Checks if a multipart-request file item exists and returns it.<p>
508     *
509     * @return <code>true</code> if a multipart-request file exists
510     */
511    private DiskFileItem getHttpImportFileItem() {
512
513        DiskFileItem result = null;
514        m_htmlimport.setHttpDir("");
515        // get the file item from the multipart-request
516        Iterator it = getMultiPartFileItems().iterator();
517        DiskFileItem fi = null;
518        while (it.hasNext()) {
519            fi = (DiskFileItem)it.next();
520            if (fi.getName() != null) {
521                // found the file object, leave iteration
522                break;
523            } else {
524                // this is no file object, check next item
525                continue;
526            }
527        }
528
529        if ((fi != null) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(fi.getName())) {
530            result = fi;
531        }
532        return result;
533    }
534
535    /**
536     * Returns a list with all available local's.<p>
537     *
538     * @return a list with all available local's
539     */
540    private List getLocales() {
541
542        ArrayList ret = new ArrayList();
543
544        try {
545            Iterator i = OpenCms.getLocaleManager().getAvailableLocales().iterator();
546
547            // loop through all local's and build the entries
548            while (i.hasNext()) {
549                Locale locale = (Locale)i.next();
550                String language = locale.getLanguage();
551                String displayLanguage = locale.getDisplayLanguage();
552
553                ret.add(new CmsSelectWidgetOption(language, false, displayLanguage));
554            }
555        } catch (Exception e) {
556            // not necessary
557        }
558        return ret;
559    }
560
561    /**
562     * Returns a list with all available templates.<p>
563     *
564     * @return a list with all available templates
565     */
566    private List getTemplates() {
567
568        ArrayList ret = new ArrayList();
569        TreeMap templates = null;
570
571        try {
572            templates = CmsNewResourceXmlPage.getTemplates(getJsp().getCmsObject(), null);
573
574            // loop through all templates and build the entries
575            Iterator i = templates.entrySet().iterator();
576            while (i.hasNext()) {
577                Map.Entry entry = (Map.Entry)i.next();
578                String title = (String)entry.getKey();
579                String path = (String)entry.getValue();
580
581                ret.add(new CmsSelectWidgetOption(path, false, title));
582            }
583        } catch (CmsException e) {
584            // not necessary
585        }
586
587        return ret;
588    }
589
590    /**
591     * Checks if given mode is to show.<p>
592     *
593     * @param mode [ {@link #MODE_DEFAULT} | {@link #MODE_STANDARD} | {@link #MODE_ADVANCED} ]
594     *
595     * @return <code>true</code> if the given display mode is to shown
596     */
597    private boolean isDisplayMode(String mode) {
598
599        return m_dialogMode.equals(mode);
600    }
601
602    /**
603     * This function reads the file item and if its exits then the
604     * file is saved in the temporary directory of the system.<p>
605     *
606     * @param fi the file item from the multipart-request
607     *
608     * @throws CmsException if something goes wrong.
609     */
610    private void writeHttpImportDir(DiskFileItem fi) throws CmsException {
611
612        try {
613
614            if ((fi != null) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(fi.getName())) {
615                //write the file in the tmp-directory of the system
616                byte[] content = fi.get();
617                File importFile = File.createTempFile("import_html", ".zip");
618                //write the content in the tmp file
619                FileOutputStream fileOutput = new FileOutputStream(importFile.getAbsolutePath());
620                fileOutput.write(content);
621                fileOutput.close();
622                fi.delete();
623                m_htmlimport.setHttpDir(importFile.getAbsolutePath());
624            }
625        } catch (Exception e) {
626            throw new CmsException(Messages.get().container(Messages.ERR_ACTION_ZIPFILE_UPLOAD_0));
627        }
628    }
629}