/* * SiteEditFormHandler.java * * This work is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * This work is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * Copyright (c) 2004-2006 Per Cederberg. All rights reserved. */ package org.liquidsite.app.admin; import java.io.IOException; import java.util.Date; import java.util.Iterator; import java.util.Map; import org.liquidsite.app.admin.view.AdminView; import org.liquidsite.core.content.Content; import org.liquidsite.core.content.ContentException; import org.liquidsite.core.content.ContentFile; import org.liquidsite.core.content.ContentFolder; import org.liquidsite.core.content.ContentPage; import org.liquidsite.core.content.ContentSecurityException; import org.liquidsite.core.content.ContentSite; import org.liquidsite.core.content.ContentTemplate; import org.liquidsite.core.content.ContentTranslator; import org.liquidsite.core.content.Domain; import org.liquidsite.core.web.FormValidationException; import org.liquidsite.core.web.FormValidator; import org.liquidsite.core.web.Request; import org.liquidsite.core.web.Request.FileParameter; /** * The site edit request handler. This class handles the edit * workflow for the site view. * * @author Per Cederberg, <per at percederberg dot net> * @version 1.0 */ class SiteEditFormHandler extends AdminFormHandler { /*** * The latest object instance created. */ private static SiteEditFormHandler instance = null; /** * The domain form validator. */ private FormValidator domainValidator = new FormValidator(); /** * The site form validator. */ private FormValidator siteValidator = new FormValidator(); /** * The folder form validator. */ private FormValidator folderValidator = new FormValidator(); /** * The page form validator. */ private FormValidator pageValidator = new FormValidator(); /** * The file form validator. */ private FormValidator fileValidator = new FormValidator(); /** * The translator form validator. */ private FormValidator translatorValidator = new FormValidator(); /** * The template form validator. */ private FormValidator templateValidator = new FormValidator(); /** * Returns an instance of this class. If a prior instance has * been created, it will be returned instead of creating a new * one. * * @return an instance of a site edit form handler */ public static SiteEditFormHandler getInstance() { if (instance == null) { return new SiteEditFormHandler(); } else { return instance; } } /** * Creates a new site edit request handler. */ public SiteEditFormHandler() { super("site.html", "edit-site.html", true); instance = this; initialize(); } /** * Initializes all the form validators. */ private void initialize() { String error; // Add and edit domain validator error = "No domain name specified"; domainValidator.addRequiredConstraint("name", error); error = "Domain name cannot be longer than 30 characters"; domainValidator.addLengthConstraint("name", 0, 30, error); error = "Domain name must be upper-case, invalid character"; domainValidator.addCharacterConstraint("name", Domain.NAME_CHARS, error); error = "No description specified"; domainValidator.addRequiredConstraint("description", error); error = "Host name must be lower-case, invalid character"; domainValidator.addCharacterConstraint("host.0.name", Domain.HOST_NAME_CHARS, error); domainValidator.addCharacterConstraint("host.1.name", Domain.HOST_NAME_CHARS, error); domainValidator.addCharacterConstraint("host.2.name", Domain.HOST_NAME_CHARS, error); domainValidator.addCharacterConstraint("host.3.name", Domain.HOST_NAME_CHARS, error); domainValidator.addCharacterConstraint("host.4.name", Domain.HOST_NAME_CHARS, error); // Add and edit site validator error = "No site name specified"; siteValidator.addRequiredConstraint("name", error); error = "No protocol specified"; siteValidator.addRequiredConstraint("protocol", error); error = "Protocol must be either 'http' or 'https', " + "invalid character"; siteValidator.addCharacterConstraint("protocol", "https", error); error = "No host name specified"; siteValidator.addRequiredConstraint("host", error); error = "Host name must be lower-case, invalid character"; siteValidator.addCharacterConstraint("host", Domain.HOST_NAME_CHARS + "*", error); error = "No port number specified"; siteValidator.addRequiredConstraint("port", error); error = "Port number must be numeric, invalid character"; siteValidator.addCharacterConstraint("port", "0123456789", error); error = "No base directory specified"; siteValidator.addRequiredConstraint("dir", error); error = "Base directory contains invalid character"; siteValidator.addCharacterConstraint("dir", Content.NAME_CHARS + "/", error); // Add and edit folder validator error = "No folder name specified"; folderValidator.addRequiredConstraint("name", error); error = "Folder name contains invalid character"; folderValidator.addCharacterConstraint("name", Content.NAME_CHARS, error); // Add and edit page validator error = "No page name specified"; pageValidator.addRequiredConstraint("name", error); error = "Page name contains invalid character"; pageValidator.addCharacterConstraint("name", Content.NAME_CHARS, error); // Add and edit file validator error = "No file name specified"; fileValidator.addRequiredConstraint("name", error); error = "File name contains invalid character"; fileValidator.addCharacterConstraint("name", Content.NAME_CHARS, error); // Add and edit translator validator error = "No translator name specified"; translatorValidator.addRequiredConstraint("name", error); error = "File name contains invalid character"; translatorValidator.addCharacterConstraint("name", Content.NAME_CHARS, error); // Add and edit template validator error = "No template name specified"; templateValidator.addRequiredConstraint("name", error); error = "Template name contains invalid character"; templateValidator.addCharacterConstraint("name", Content.NAME_CHARS, error); } /** * Displays a form for the specified workflow step. This method * will NOT be called when returning to the start page. * * @param request the request object * @param step the workflow step * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ protected void displayStep(Request request, int step) throws ContentException, ContentSecurityException { Object ref = AdminUtils.getReference(request); if (ref instanceof Domain) { AdminView.SITE.viewEditDomain(request, null, (Domain) ref); } else if (ref instanceof ContentSite) { AdminView.SITE.viewEditSite(request, (ContentSite) ref); } else if (ref instanceof ContentFolder) { AdminView.SITE.viewEditFolder(request, null, (ContentFolder) ref); } else if (ref instanceof ContentPage) { AdminView.SITE.viewEditPage(request, (ContentPage) ref); } else if (ref instanceof ContentFile) { AdminView.SITE.viewEditFile(request, (ContentFile) ref); } else if (ref instanceof ContentTranslator) { AdminView.SITE.viewEditTranslator(request, (ContentTranslator) ref); } else if (ref instanceof ContentTemplate) { AdminView.SITE.viewEditTemplate(request, null, (ContentTemplate) ref); } else { throw new ContentException("cannot edit this object"); } } /** * Validates a form for the specified workflow step. If the form * validation fails in this step, the form page for the workflow * step will be displayed again with an 'error' attribute * containing the message in the validation exception. * * @param request the request object * @param step the workflow step * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions * @throws FormValidationException if the form request data * validation failed */ protected void validateStep(Request request, int step) throws ContentException, ContentSecurityException, FormValidationException { String category = request.getParameter("category", ""); String message; if (category.equals("domain")) { domainValidator.validate(request); if (!request.getParameter("name").equals("ROOT") && request.getParameter("host.0.name", "").length() <= 0) { message = "No host name specified"; throw new FormValidationException("host.0.name", message); } } else if (category.equals("site")) { siteValidator.validate(request); // TODO: check parameters for conflicting sites & objects } else if (category.equals("folder")) { folderValidator.validate(request); message = "Another object with identical name already " + "exists in the parent folder"; validateParent(request, "parent", message); } else if (category.equals("page")) { pageValidator.validate(request); message = "Another object with identical name already " + "exists in the parent folder"; validateParent(request, "parent", message); } else if (category.equals("file")) { validateFile(request); message = "Another object with identical name already " + "exists in the parent folder"; validateParent(request, "parent", message); } else if (category.equals("translator")) { translatorValidator.validate(request); message = "Another object with identical name already " + "exists in the parent folder"; validateParent(request, "parent", message); } else if (category.equals("template")) { templateValidator.validate(request); message = "Another object with identical name already " + "exists in the base template or domain"; validateParent(request, "parent", message); } else { message = "Unknown content category specified"; throw new FormValidationException("category", message); } } /** * Validates a file edit form. * * @param request the request object * * @throws FormValidationException if the form request data * validation failed */ protected void validateFile(Request request) throws FormValidationException { fileValidator.validate(request); } /** * Handles a validated form for the specified workflow step. This * method returns the next workflow step, i.e. the step used when * calling the display method. If the special zero (0) workflow * step is returned, the workflow is assumed to have terminated. * Note that this method also allows additional validation to * occur. By returning the incoming workflow step number and * setting the appropriate request attributes the same results as * in the normal validate method can be achieved. For recoverable * errors, this is the recommended course of action. * * @param request the request object * @param step the workflow step * * @return the next workflow step, or * zero (0) if the workflow has finished * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ protected int handleStep(Request request, int step) throws ContentException, ContentSecurityException { Object ref = AdminUtils.getReference(request); if (ref instanceof Domain) { handleEditDomain(request, (Domain) ref); } else if (ref instanceof ContentSite) { handleEditSite(request, (ContentSite) ref); } else if (ref instanceof ContentFolder) { handleEditFolder(request, (ContentFolder) ref); } else if (ref instanceof ContentPage) { handleEditPage(request, (ContentPage) ref); } else if (ref instanceof ContentFile) { handleEditFile(request, (ContentFile) ref); } else if (ref instanceof ContentTranslator) { handleEditTranslator(request, (ContentTranslator) ref); } else if (ref instanceof ContentTemplate) { handleEditTemplate(request, (ContentTemplate) ref); } else { throw new ContentException("cannot edit this object"); } return 0; } /** * Handles the edit domain form. * * @param request the request object * @param domain the domain object * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ private void handleEditDomain(Request request, Domain domain) throws ContentException, ContentSecurityException { Iterator iter; String param; String name; String descr; domain.setDescription(request.getParameter("description")); domain.setMailFrom(request.getParameter("mailaddress")); domain.removeAllHosts(); iter = request.getAllParameters().keySet().iterator(); while (iter.hasNext()) { param = iter.next().toString(); if (param.startsWith("host.") && param.endsWith(".name")) { param = param.substring(0, param.length() - 5); name = request.getParameter(param + ".name"); descr = request.getParameter(param + ".description"); domain.addHost(name, descr); } } domain.save(request.getUser()); } /** * Handles the edit site form. * * @param request the request object * @param site the site object * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ private void handleEditSite(Request request, ContentSite site) throws ContentException, ContentSecurityException { site.setRevisionNumber(0); site.setName(request.getParameter("name")); site.setProtocol(request.getParameter("protocol")); site.setHost(request.getParameter("host")); site.setPort(Integer.parseInt(request.getParameter("port"))); site.setDirectory(request.getParameter("dir")); site.setComment(request.getParameter("comment")); if (request.getParameter("action", "").equals("publish")) { site.setRevisionNumber(site.getMaxRevisionNumber() + 1); site.setOnlineDate(new Date()); site.setOfflineDate(null); } site.save(request.getUser()); } /** * Handles the edit folder form. * * @param request the request object * @param folder the folder content object * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ private void handleEditFolder(Request request, ContentFolder folder) throws ContentException, ContentSecurityException { int id; folder.setRevisionNumber(0); folder.setName(request.getParameter("name")); try { id = Integer.parseInt(request.getParameter("parent")); folder.setParentId(id); } catch (NumberFormatException ignore) { // This is ignored } folder.setComment(request.getParameter("comment")); if (request.getParameter("action", "").equals("publish")) { folder.setRevisionNumber(folder.getMaxRevisionNumber() + 1); folder.setOnlineDate(new Date()); folder.setOfflineDate(null); } folder.save(request.getUser()); } /** * Handles the edit page form. * * @param request the request object * @param page the page content object * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ private void handleEditPage(Request request, ContentPage page) throws ContentException, ContentSecurityException { Map params = request.getAllParameters(); Iterator iter = params.keySet().iterator(); String name; String value; int id; page.setRevisionNumber(0); page.setName(request.getParameter("name")); try { id = Integer.parseInt(request.getParameter("parent")); page.setParentId(id); } catch (NumberFormatException ignore) { // This is ignored } try { id = Integer.parseInt(request.getParameter("template")); } catch (NumberFormatException ignore) { id = 0; } page.setTemplateId(id); page.setComment(request.getParameter("comment")); while (iter.hasNext()) { name = iter.next().toString(); if (name.startsWith("element.")) { value = params.get(name).toString(); page.setElement(name.substring(8), value); } } iter = page.getLocalElementNames().iterator(); while (iter.hasNext()) { name = iter.next().toString(); if (!params.containsKey("element." + name)) { page.setElement(name, null); } } if (request.getParameter("action", "").equals("publish")) { page.setRevisionNumber(page.getMaxRevisionNumber() + 1); page.setOnlineDate(new Date()); page.setOfflineDate(null); } page.save(request.getUser()); } /** * Handles the edit file form. * * @param request the request object * @param file the file content object * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ private void handleEditFile(Request request, ContentFile file) throws ContentException, ContentSecurityException { FileParameter param; int id; String name; try { file.setRevisionNumber(0); file.setName(request.getParameter("name")); try { id = Integer.parseInt(request.getParameter("parent")); file.setParentId(id); } catch (NumberFormatException ignore) { // This is ignored } file.setComment(request.getParameter("comment")); param = request.getFileParameter("upload"); if (param != null && param.getSize() > 0) { file.setFileName(param.getName()); param.write(file.getFile()); } else if (request.getParameter("content") != null) { name = file.getFileName(); name = name.substring(name.indexOf(".") + 1); if (name.indexOf(".") <= 0) { file.setFileName(file.getFileName()); } else { file.setFileName(name); } file.setTextContent(request.getParameter("content")); } if (request.getParameter("action", "").equals("publish")) { file.setRevisionNumber(file.getMaxRevisionNumber() + 1); file.setOnlineDate(new Date()); file.setOfflineDate(null); } file.save(request.getUser()); } catch (IOException e) { throw new ContentException(e.getMessage()); } } /** * Handles the edit translator form. * * @param request the request object * @param translator the translator content object * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ private void handleEditTranslator(Request request, ContentTranslator translator) throws ContentException, ContentSecurityException { int id; translator.setRevisionNumber(0); translator.setName(request.getParameter("name")); try { id = Integer.parseInt(request.getParameter("parent")); translator.setParentId(id); } catch (NumberFormatException ignore) { // This is ignored } try { id = Integer.parseInt(request.getParameter("section")); } catch (NumberFormatException ignore) { id = 0; } translator.setType(ContentTranslator.SECTION_TYPE); translator.setSectionId(id); translator.setComment(request.getParameter("comment")); if (request.getParameter("action", "").equals("publish")) { id = translator.getMaxRevisionNumber() + 1; translator.setRevisionNumber(id); translator.setOnlineDate(new Date()); translator.setOfflineDate(null); } translator.save(request.getUser()); } /** * Handles the edit template form. * * @param request the request object * @param template the template content object * * @throws ContentException if the database couldn't be accessed * properly * @throws ContentSecurityException if the user didn't have the * required permissions */ private void handleEditTemplate(Request request, ContentTemplate template) throws ContentException, ContentSecurityException { Map params = request.getAllParameters(); Iterator iter = params.keySet().iterator(); String name; String value; int parent; template.setRevisionNumber(0); template.setName(request.getParameter("name")); template.setComment(request.getParameter("comment")); try { parent = Integer.parseInt(request.getParameter("parent")); template.setParentId(parent); } catch (NumberFormatException ignore) { // This is ignored } while (iter.hasNext()) { name = iter.next().toString(); if (name.startsWith("element.")) { value = params.get(name).toString(); template.setElement(name.substring(8), value); } } iter = template.getLocalElementNames().iterator(); while (iter.hasNext()) { name = iter.next().toString(); if (!params.containsKey("element." + name)) { template.setElement(name, null); } } if (request.getParameter("action", "").equals("publish")) { template.setRevisionNumber(template.getMaxRevisionNumber() + 1); template.setOnlineDate(new Date()); template.setOfflineDate(null); } template.save(request.getUser()); } }