001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.cocoon; 017 018import java.io.File; 019import java.io.IOException; 020import java.util.Map; 021import java.util.MissingResourceException; 022 023import org.apache.avalon.framework.component.ComponentException; 024import org.apache.avalon.framework.configuration.Configuration; 025import org.apache.avalon.framework.configuration.ConfigurationException; 026import org.apache.avalon.framework.configuration.DefaultConfiguration; 027import org.apache.avalon.framework.parameters.Parameters; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.cocoon.ProcessingException; 031import org.apache.cocoon.environment.ObjectModelHelper; 032import org.apache.cocoon.environment.Request; 033import org.apache.cocoon.environment.SourceResolver; 034import org.apache.cocoon.xml.ParamSaxBuffer; 035import org.apache.commons.lang.StringUtils; 036import org.xml.sax.SAXException; 037 038import org.ametys.core.cocoon.XMLResourceBundle; 039import org.ametys.web.skin.SkinsManager; 040 041/** 042 * CMS version of the I18nTransformer, dealing with skin catalogues.<br> 043 * It is also able to reconfigure itself dynamically, to reflect changes. 044 */ 045public class I18nTransformer extends org.ametys.core.cocoon.I18nTransformer 046{ 047 // The last time, "needsReload" was called 048 private static long _needsReloadTime; 049 050 /** the avalon service manager */ 051 protected ServiceManager _manager; 052 053 private Configuration _conf; 054 055 /** The time the configure method was lastly called on this instance */ 056 private long _lastConfigureTime; 057 058 private SkinsManager _skinsManager; 059 060 @Override 061 public void configure(Configuration conf) throws ConfigurationException 062 { 063 _conf = conf; 064 _configure(); 065 } 066 067 @Override 068 public void service(ServiceManager smanager) throws ServiceException 069 { 070 super.service(smanager); 071 _manager = smanager; 072 _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE); 073 } 074 075 private void _configure() throws ConfigurationException 076 { 077 _lastConfigureTime = System.currentTimeMillis(); 078 079 // Modification de la configuration pour faire apparaitre les sites 080 DefaultConfiguration newConf = new DefaultConfiguration("i18n"); 081 082 Configuration cataloguesConf = _conf.getChild("catalogues", false); 083 DefaultConfiguration catalogues = new DefaultConfiguration("catalogues"); 084 newConf.addChild(catalogues); 085 086 if (cataloguesConf != null) 087 { 088 catalogues.addAll(cataloguesConf); 089 } 090 091 _configureCatalogue(catalogues, "skins", "skin"); 092 _configureCatalogue(catalogues, "models", "model"); 093 094 super.configure(newConf); 095 } 096 097 private void _configureCatalogue(DefaultConfiguration catalogues, String dirName, String catalogName) 098 { 099 File skinsBaseDir = new File(_context.getRealPath("/" + dirName)); 100 101 if (skinsBaseDir.exists()) 102 { 103 File[] skinsDir = skinsBaseDir.listFiles(); 104 105 for (int i = 0; i < skinsDir.length; i++) 106 { 107 File skinDir = skinsDir[i]; 108 109 // On filtre les non-repertoires, non-plugins, et les plugins sans i18n 110 if (skinDir.isDirectory() && new File(skinDir, "i18n/messages.xml").exists()) 111 { 112 String name = skinDir.getName(); 113 String id = catalogName + "." + name; 114 115 DefaultConfiguration catalogue = new DefaultConfiguration("catalogue"); 116 catalogue.setAttribute("id", id); 117 catalogue.setAttribute("name", "messages"); 118 catalogue.setAttribute("location", "context://" + dirName + "/" + name + "/i18n"); 119 120 catalogues.addChild(catalogue); 121 } 122 } 123 } 124 } 125 126 @Override 127 protected ParamSaxBuffer getMessage(String catalogueID, String key) 128 { 129 Request request = ObjectModelHelper.getRequest(objectModel); 130 131 String skinLocation = (String) request.getAttribute("skin-location"); 132 if (skinLocation != null && catalogueID != null && catalogueID.startsWith("skin")) 133 { 134 ParamSaxBuffer message = null; 135 136 String skinName = _skinsManager.getSkinNameFromRequest (request); 137 138 if (StringUtils.isNotEmpty(skinName)) 139 { 140 message = _getExternalMessage ("ametys-home://" + skinLocation + "/" + skinName + "/i18n", key); 141 } 142 143 if (message == null) 144 { 145 message = getUntranslatedMessage (catalogueID, key); 146 } 147 148 return message; 149 } 150 else 151 { 152 return super.getMessage(catalogueID, key); 153 } 154 } 155 156 private ParamSaxBuffer _getExternalMessage (String externalLocation, String key) 157 { 158 try 159 { 160 XMLResourceBundle bundle = (XMLResourceBundle) factory.select(externalLocation, "messages", locale); 161 if (bundle == null) 162 { 163 // Can't translate 164 if (getLogger().isDebugEnabled()) 165 { 166 getLogger().debug("No bundle found for catalogue location '" + externalLocation + "'"); 167 } 168 return null; 169 } 170 171 try 172 { 173 return (ParamSaxBuffer) bundle.getObject(key); 174 } 175 catch (MissingResourceException e) 176 { 177 if (getLogger().isDebugEnabled()) 178 { 179 getLogger().debug("Untranslated key: '" + key + "'"); 180 } 181 } 182 } 183 catch (ComponentException e) 184 { 185 if (getLogger().isDebugEnabled()) 186 { 187 getLogger().debug("Untranslated key: '" + key + "'"); 188 } 189 } 190 191 return null; 192 193 } 194 195 /** 196 * Call this method when you change the list of i18n catalogues <br> by adding a new skin for example. 197 */ 198 public static void needsReload() 199 { 200 _needsReloadTime = System.currentTimeMillis(); 201 } 202 203 @Override 204 public void setup(SourceResolver resolver, Map objModel, String source, Parameters parameters) throws ProcessingException, SAXException, IOException 205 { 206 try 207 { 208 if (_needsReloadTime > _lastConfigureTime) 209 { 210 _configure(); 211 } 212 } 213 catch (ConfigurationException e) 214 { 215 String errorMessage = "Error with i18n on work dir"; 216 getLogger().error(errorMessage, e); 217 throw new SAXException(errorMessage, e); 218 } 219 220 super.setup(resolver, objModel, source, parameters); 221 } 222}