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.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 javax.servlet.http.HttpServletRequest;
062import javax.servlet.http.HttpServletResponse;
063import javax.servlet.jsp.PageContext;
064
065import org.apache.commons.fileupload.FileItem;
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                FileItem 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(getDialogParameter(
285                "destinationDir",
286                new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
287        }
288
289        addWidget(
290            getDialogParameter(
291                "imageGallery",
292                new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
293        addWidget(
294            getDialogParameter(
295                "downloadGallery",
296                new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
297        addWidget(
298            getDialogParameter("linkGallery", new CmsVfsFileWidget(false, getCms().getRequestContext().getSiteRoot())));
299
300        addWidget(getDialogParameter("template", new CmsSelectWidget(getTemplates())));
301        addWidget(getDialogParameter("element", new CmsInputWidget()));
302        addWidget(getDialogParameter("locale", new CmsSelectWidget(getLocales())));
303        addWidget(getDialogParameter("inputEncoding", new CmsInputWidget()));
304        addWidget(getDialogParameter("startPattern", new CmsInputWidget()));
305        addWidget(getDialogParameter("endPattern", new CmsInputWidget()));
306
307        addWidget(getDialogParameter("overwrite", new CmsCheckboxWidget()));
308        addWidget(getDialogParameter("keepBrokenLinks", new CmsCheckboxWidget()));
309    }
310
311    /**
312     * This function fills the <code> {@link CmsHtmlImport} </code> Object based on
313     * the values in the import/export configuration file. <p>
314     */
315    protected void fillHtmlImport() {
316
317        CmsExtendedHtmlImportDefault extimport = OpenCms.getImportExportManager().getExtendedHtmlImportDefault();
318        m_htmlimport.setDestinationDir(extimport.getDestinationDir());
319        m_htmlimport.setInputDir(extimport.getInputDir());
320        m_htmlimport.setDownloadGallery(extimport.getDownloadGallery());
321        m_htmlimport.setImageGallery(extimport.getImageGallery());
322        m_htmlimport.setLinkGallery(extimport.getLinkGallery());
323        m_htmlimport.setTemplate(extimport.getTemplate());
324        m_htmlimport.setElement(extimport.getElement());
325        m_htmlimport.setLocale(extimport.getLocale());
326        m_htmlimport.setInputEncoding(extimport.getEncoding());
327        m_htmlimport.setStartPattern(extimport.getStartPattern());
328        m_htmlimport.setEndPattern(extimport.getEndPattern());
329        m_htmlimport.setOverwrite(Boolean.valueOf(extimport.getOverwrite()).booleanValue());
330        m_htmlimport.setKeepBrokenLinks(Boolean.valueOf(extimport.getKeepBrokenLinks()).booleanValue());
331    }
332
333    /**
334     * @see org.opencms.workplace.CmsWidgetDialog#fillWidgetValues(javax.servlet.http.HttpServletRequest)
335     */
336    @Override
337    protected void fillWidgetValues(HttpServletRequest request) {
338
339        Map parameters;
340        if (getMultiPartFileItems() != null) {
341            parameters = CmsRequestUtil.readParameterMapFromMultiPart(
342                getCms().getRequestContext().getEncoding(),
343                getMultiPartFileItems());
344        } else {
345            parameters = request.getParameterMap();
346        }
347        Map processedParameters = new HashMap();
348        Iterator p = parameters.entrySet().iterator();
349        // make sure all "hidden" widget parameters are decoded
350        while (p.hasNext()) {
351            Map.Entry entry = (Map.Entry)p.next();
352            String key = (String)entry.getKey();
353            String[] values = (String[])entry.getValue();
354            if (key.startsWith(HIDDEN_PARAM_PREFIX)) {
355                // this is an encoded hidden parameter
356                key = key.substring(HIDDEN_PARAM_PREFIX.length());
357                String[] newValues = new String[values.length];
358                for (int l = 0; l < values.length; l++) {
359                    newValues[l] = CmsEncoder.decode(values[l], getCms().getRequestContext().getEncoding());
360                }
361                values = newValues;
362            }
363            processedParameters.put(key, values);
364        }
365
366        // now process the parameters
367        m_widgetParamValues = new HashMap();
368        Iterator i = getWidgets().iterator();
369
370        while (i.hasNext()) {
371            // check for all widget base parameters
372            CmsWidgetDialogParameter base = (CmsWidgetDialogParameter)i.next();
373
374            List params = new ArrayList();
375            int maxOccurs = base.getMaxOccurs();
376
377            boolean onPage = false;
378            if (base.isCollectionBase()) {
379                // for a collection base, check if we are on the page where the collection base is shown
380                if (CmsStringUtil.isNotEmpty(getParamAction()) && !DIALOG_INITIAL.equals(getParamAction())) {
381                    // if no action set (usually for first display of dialog) make sure all values are shown
382                    // DIALOG_INITIAL is a special value for the first display and must be handled the same way
383                    String page = getParamPage();
384                    // keep in mind that since the paramPage will be set AFTER the widget values are filled,
385                    // so the first time this page is called from another page the following will result to "false",
386                    // but for every "submit" on the page this will be "true"
387                    onPage = CmsStringUtil.isEmpty(page)
388                        || CmsStringUtil.isEmpty(base.getDialogPage())
389                        || base.getDialogPage().equals(page);
390                }
391            }
392
393            for (int j = 0; j < maxOccurs; j++) {
394                // check for all possible values in the request parameters
395                String id = CmsWidgetDialogParameter.createId(base.getName(), j);
396
397                boolean required = (params.size() < base.getMinOccurs())
398                    || (processedParameters.get(id) != null)
399                    || (!onPage && base.hasValue(j));
400
401                if (required) {
402                    CmsWidgetDialogParameter param = new CmsWidgetDialogParameter(base, params.size(), j);
403                    param.setKeyPrefix(KEY_PREFIX);
404                    base.getWidget().setEditorValue(getCms(), processedParameters, this, param);
405                    params.add(param);
406                }
407            }
408            m_widgetParamValues.put(base.getName(), params);
409        }
410    }
411
412    /**
413     * This function creates a <code> {@link CmsWidgetDialogParameter} </code> Object based
414     * on the given properties.<p>
415     *
416     * @param property the base object property to map the parameter to / from
417     * @param widget the widget used for this dialog-parameter
418     *
419     * @return a <code> {@link CmsWidgetDialogParameter} </code> Object
420     */
421    protected CmsWidgetDialogParameter getDialogParameter(String property, I_CmsWidget widget) {
422
423        return new CmsWidgetDialogParameter(m_htmlimport, property, PAGES[0], widget);
424    }
425
426    /**
427     * @see org.opencms.workplace.CmsWidgetDialog#getPageArray()
428     */
429    @Override
430    protected String[] getPageArray() {
431
432        return PAGES;
433    }
434
435    /**
436     * Initializes this widget dialog's object.<p>
437     */
438    protected void initHtmlImportObject() {
439
440        Object o;
441        String uri = getJsp().getRequestContext().getUri();
442        if ((uri == null) || uri.endsWith(IMPORT_STANDARD_PATH)) {
443            m_dialogMode = MODE_STANDARD;
444        } else if (uri.endsWith(IMPORT_DEFAULT_PATH)) {
445            m_dialogMode = MODE_DEFAULT;
446        } else {
447            m_dialogMode = MODE_ADVANCED;
448        }
449        if (CmsStringUtil.isEmpty(getParamAction())) {
450            o = new CmsHtmlImport(getJsp().getCmsObject());
451        } else {
452            // this is not the initial call, get the job object from session
453            o = getDialogObject();
454        }
455
456        if (!(o instanceof CmsHtmlImport)) {
457            // create a new HTML import handler object
458            m_htmlimport = new CmsHtmlImport(getJsp().getCmsObject());
459        } else {
460            // reuse HTML import handler object stored in session
461            m_htmlimport = (CmsHtmlImport)o;
462            // this is needed, because the user can switch between the sites, now get the current
463            m_htmlimport.setCmsObject(getJsp().getCmsObject());
464        }
465
466        // gets the data from the configuration file
467        fillHtmlImport();
468    }
469
470    /**
471     * @see org.opencms.workplace.CmsWorkplace#initMessages()
472     */
473    @Override
474    protected void initMessages() {
475
476        // add specific dialog resource bundle
477        addMessages(Messages.get().getBundleName());
478        // add default resource bundles
479        super.initMessages();
480    }
481
482    /**
483     * This function fills the <code> {@link CmsExtendedHtmlImportDefault} </code> Object based on
484     * the current values in the dialog. <p>
485     */
486    private void fillExtendedHtmlImportDefault() {
487
488        CmsExtendedHtmlImportDefault extimport = OpenCms.getImportExportManager().getExtendedHtmlImportDefault();
489        extimport.setDestinationDir(m_htmlimport.getDestinationDir());
490        extimport.setInputDir(m_htmlimport.getInputDir());
491        extimport.setDownloadGallery(m_htmlimport.getDownloadGallery());
492        extimport.setImageGallery(m_htmlimport.getImageGallery());
493        extimport.setLinkGallery(m_htmlimport.getLinkGallery());
494        extimport.setTemplate(m_htmlimport.getTemplate());
495        extimport.setElement(m_htmlimport.getElement());
496        extimport.setLocale(m_htmlimport.getLocale());
497        extimport.setEncoding(m_htmlimport.getInputEncoding());
498        extimport.setStartPattern(m_htmlimport.getStartPattern());
499        extimport.setEndPattern(m_htmlimport.getEndPattern());
500        extimport.setOverwrite(Boolean.toString(m_htmlimport.isOverwrite()));
501        extimport.setKeepBrokenLinks(Boolean.toString(m_htmlimport.isKeepBrokenLinks()));
502        OpenCms.getImportExportManager().setExtendedHtmlImportDefault(extimport);
503    }
504
505    /**
506     * Returns a list with all available local's.<p>
507     *
508     * @return a list with all available local's
509     */
510    private List getLocales() {
511
512        ArrayList ret = new ArrayList();
513
514        try {
515            Iterator i = OpenCms.getLocaleManager().getAvailableLocales().iterator();
516
517            // loop through all local's and build the entries
518            while (i.hasNext()) {
519                Locale locale = (Locale)i.next();
520                String language = locale.getLanguage();
521                String displayLanguage = locale.getDisplayLanguage();
522
523                ret.add(new CmsSelectWidgetOption(language, false, displayLanguage));
524            }
525        } catch (Exception e) {
526            // not necessary
527        }
528        return ret;
529    }
530
531    /**
532     * Returns a list with all available templates.<p>
533     *
534     * @return a list with all available templates
535     */
536    private List getTemplates() {
537
538        ArrayList ret = new ArrayList();
539        TreeMap templates = null;
540
541        try {
542            templates = CmsNewResourceXmlPage.getTemplates(getJsp().getCmsObject(), null);
543
544            // loop through all templates and build the entries
545            Iterator i = templates.entrySet().iterator();
546            while (i.hasNext()) {
547                Map.Entry entry = (Map.Entry)i.next();
548                String title = (String)entry.getKey();
549                String path = (String)entry.getValue();
550
551                ret.add(new CmsSelectWidgetOption(path, false, title));
552            }
553        } catch (CmsException e) {
554            // not necessary
555        }
556
557        return ret;
558    }
559
560    /**
561     * Checks if given mode is to show.<p>
562     *
563     * @param mode [ {@link #MODE_DEFAULT} | {@link #MODE_STANDARD} | {@link #MODE_ADVANCED} ]
564     *
565     * @return <code>true</code> if the given display mode is to shown
566     */
567    private boolean isDisplayMode(String mode) {
568
569        return m_dialogMode.equals(mode);
570    }
571
572    /**
573     * This function reads the file item and if its exits then the
574     * file is saved in the temporary directory of the system.<p>
575     *
576     * @param fi the file item from the multipart-request
577     *
578     * @throws CmsException if something goes wrong.
579     */
580    private void writeHttpImportDir(FileItem fi) throws CmsException {
581
582        try {
583
584            if ((fi != null) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(fi.getName())) {
585                //write the file in the tmp-directory of the system
586                byte[] content = fi.get();
587                File importFile = File.createTempFile("import_html", ".zip");
588                //write the content in the tmp file
589                FileOutputStream fileOutput = new FileOutputStream(importFile.getAbsolutePath());
590                fileOutput.write(content);
591                fileOutput.close();
592                fi.delete();
593                m_htmlimport.setHttpDir(importFile.getAbsolutePath());
594            }
595        } catch (Exception e) {
596            throw new CmsException(Messages.get().container(Messages.ERR_ACTION_ZIPFILE_UPLOAD_0));
597        }
598    }
599
600    /**
601     * Checks if a multipart-request file item exists and returns it.<p>
602     *
603     * @return <code>true</code> if a multipart-request file exists
604     */
605    private FileItem getHttpImportFileItem() {
606
607        FileItem result = null;
608        m_htmlimport.setHttpDir("");
609        // get the file item from the multipart-request
610        Iterator it = getMultiPartFileItems().iterator();
611        FileItem fi = null;
612        while (it.hasNext()) {
613            fi = (FileItem)it.next();
614            if (fi.getName() != null) {
615                // found the file object, leave iteration
616                break;
617            } else {
618                // this is no file object, check next item
619                continue;
620            }
621        }
622
623        if ((fi != null) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(fi.getName())) {
624            result = fi;
625        }
626        return result;
627    }
628}