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.xml.containerpage; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.main.CmsException; 034import org.opencms.main.CmsLog; 035import org.opencms.relations.CmsLink; 036import org.opencms.xml.CmsXmlException; 037import org.opencms.xml.CmsXmlUtils; 038import org.opencms.xml.content.CmsDefaultXmlContentHandler; 039import org.opencms.xml.content.CmsXmlContent; 040import org.opencms.xml.content.CmsXmlContentErrorHandler; 041import org.opencms.xml.types.CmsXmlVarLinkValue; 042import org.opencms.xml.types.CmsXmlVfsFileValue; 043import org.opencms.xml.types.I_CmsXmlContentValue; 044 045import java.util.Iterator; 046import java.util.Locale; 047 048import org.apache.commons.logging.Log; 049 050/** 051 * Container page handler to validate consistency.<p> 052 * 053 * @since 7.6 054 */ 055public class CmsXmlContainerPageHandler extends CmsDefaultXmlContentHandler { 056 057 /** Logger instance for this class. */ 058 private static final Log LOG = CmsLog.getLog(CmsXmlContainerPageHandler.class); 059 060 /** 061 * Creates a new instance.<p> 062 */ 063 public CmsXmlContainerPageHandler() { 064 065 super(); 066 } 067 068 /** 069 * @see org.opencms.xml.content.CmsDefaultXmlContentHandler#hasModifiableFormatters() 070 */ 071 @Override 072 public boolean hasModifiableFormatters() { 073 074 return false; 075 } 076 077 /** 078 * @see org.opencms.xml.content.CmsDefaultXmlContentHandler#prepareForWrite(org.opencms.file.CmsObject, org.opencms.xml.content.CmsXmlContent, org.opencms.file.CmsFile) 079 */ 080 @Override 081 public CmsFile prepareForWrite(CmsObject cms, CmsXmlContent content, CmsFile file) throws CmsException { 082 083 Object attribute = cms.getRequestContext().getAttribute(CmsXmlContent.AUTO_CORRECTION_ATTRIBUTE); 084 boolean autoCorrectionEnabled = (attribute != null) && ((Boolean)attribute).booleanValue(); 085 if (autoCorrectionEnabled) { // this is to ensure that 'touch' converts pages to the V12 format. 086 CmsXmlContainerPage page = (CmsXmlContainerPage)content; 087 try { 088 page.writeContainerPage(cms, page.getContainerPage(cms)); 089 } catch (Exception e) { 090 LOG.error(e.getLocalizedMessage(), e); 091 } 092 } 093 return super.prepareForWrite(cms, content, file); 094 } 095 096 /** 097 * @see org.opencms.xml.content.I_CmsXmlContentHandler#resolveValidation(org.opencms.file.CmsObject, org.opencms.xml.types.I_CmsXmlContentValue, org.opencms.xml.content.CmsXmlContentErrorHandler) 098 */ 099 @Override 100 public CmsXmlContentErrorHandler resolveValidation( 101 CmsObject cms, 102 I_CmsXmlContentValue value, 103 CmsXmlContentErrorHandler errorHandler) { 104 105 if (errorHandler == null) { 106 // init a new error handler if required 107 errorHandler = new CmsXmlContentErrorHandler(); 108 } 109 110 // we only have to validate containers 111 if ((value != null) 112 && CmsXmlUtils.removeXpath(value.getPath()).equals(CmsXmlContainerPage.XmlNode.Containers.name())) { 113 CmsXmlContent content = (CmsXmlContent)value.getDocument(); 114 try { 115 validateNames(cms, value, content); 116 } catch (CmsXmlException e) { 117 errorHandler.addError(value, e.getLocalizedMessage()); 118 } 119 } 120 121 return errorHandler; 122 } 123 124 /** 125 * @see org.opencms.xml.content.CmsDefaultXmlContentHandler#validateLink(org.opencms.file.CmsObject, org.opencms.xml.types.I_CmsXmlContentValue, org.opencms.xml.content.CmsXmlContentErrorHandler) 126 */ 127 @Override 128 protected boolean validateLink(CmsObject cms, I_CmsXmlContentValue value, CmsXmlContentErrorHandler errorHandler) { 129 130 // if there is a value of type file reference 131 if ((value == null) || (!(value instanceof CmsXmlVfsFileValue) && !(value instanceof CmsXmlVarLinkValue))) { 132 return false; 133 } 134 // if the value has a link (this will automatically fix, for instance, the path of moved resources) 135 CmsLink link = null; 136 if (value instanceof CmsXmlVfsFileValue) { 137 link = ((CmsXmlVfsFileValue)value).getLink(cms); 138 } else if (value instanceof CmsXmlVarLinkValue) { 139 link = ((CmsXmlVarLinkValue)value).getLink(cms); 140 } 141 if ((link == null) || !link.isInternal()) { 142 return false; 143 } 144 try { 145 String sitePath = cms.getRequestContext().removeSiteRoot(link.getTarget()); 146 // validate the link for error 147 cms.readResource(sitePath, CmsResourceFilter.IGNORE_EXPIRATION); 148 149 // we handle expiration in the cms:container tag, so don't validate it here 150 151 } catch (CmsException e) { 152 if (errorHandler != null) { 153 // generate error message 154 errorHandler.addError( 155 value, 156 org.opencms.xml.content.Messages.get().getBundle(value.getLocale()).key( 157 org.opencms.xml.content.Messages.GUI_XMLCONTENT_CHECK_ERROR_0)); 158 } 159 return true; 160 } 161 return false; 162 } 163 164 /** 165 * Validates container names, so that they are unique in the page.<p> 166 * 167 * @param cms the cms context 168 * @param value the value to validate 169 * @param content the container page to validate 170 * 171 * @throws CmsXmlException if there are duplicated names 172 */ 173 protected void validateNames(CmsObject cms, I_CmsXmlContentValue value, CmsXmlContent content) 174 throws CmsXmlException { 175 176 // get the current name 177 Locale locale = value.getLocale(); 178 String namePath = CmsXmlUtils.concatXpath(value.getPath(), CmsXmlContainerPage.XmlNode.Name.name()); 179 String name = content.getValue(namePath, locale).getStringValue(cms); 180 // iterate over all containers 181 Iterator<I_CmsXmlContentValue> itValues = content.getValues( 182 CmsXmlContainerPage.XmlNode.Containers.name(), 183 locale).iterator(); 184 while (itValues.hasNext()) { 185 I_CmsXmlContentValue itValue = itValues.next(); 186 if (itValue.getPath().equals(value.getPath())) { 187 // skip current container 188 continue; 189 } 190 // get container name 191 namePath = CmsXmlUtils.concatXpath(itValue.getPath(), CmsXmlContainerPage.XmlNode.Name.name()); 192 String itName = content.getValue(namePath, locale).getStringValue(cms); 193 // validate 194 if (name.equals(itName)) { 195 throw new CmsXmlException(Messages.get().container(Messages.ERR_DUPLICATE_NAME_1, name)); 196 } 197 } 198 } 199}