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.IOException; 019import java.util.Map; 020import java.util.MissingResourceException; 021 022import org.apache.avalon.framework.component.ComponentException; 023import org.apache.avalon.framework.configuration.Configuration; 024import org.apache.avalon.framework.configuration.ConfigurationException; 025import org.apache.avalon.framework.configuration.DefaultConfiguration; 026import org.apache.avalon.framework.parameters.Parameters; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.cocoon.ProcessingException; 030import org.apache.cocoon.environment.ObjectModelHelper; 031import org.apache.cocoon.environment.Request; 032import org.apache.cocoon.environment.SourceResolver; 033import org.apache.cocoon.xml.ParamSaxBuffer; 034import org.apache.commons.lang.StringUtils; 035import org.xml.sax.SAXException; 036 037import org.ametys.core.cocoon.XMLResourceBundle; 038import org.ametys.web.skin.Skin; 039import org.ametys.web.skin.SkinModelsManager; 040import org.ametys.web.skin.SkinsManager; 041 042/** 043 * CMS version of the I18nTransformer, dealing with skin catalogues.<br> 044 * It is also able to reconfigure itself dynamically, to reflect changes. 045 */ 046public class I18nTransformer extends org.ametys.core.cocoon.I18nTransformer 047{ 048 // The last time, "needsReload" was called 049 private static long _needsReloadTime; 050 051 /** the avalon service manager */ 052 protected ServiceManager _manager; 053 054 private Configuration _conf; 055 056 /** The time the configure method was lastly called on this instance */ 057 private long _lastConfigureTime; 058 059 private SkinsManager _skinsManager; 060 private SkinModelsManager _skinModelsManager; 061 062 @Override 063 public void configure(Configuration conf) throws ConfigurationException 064 { 065 _conf = conf; 066 _configure(); 067 } 068 069 @Override 070 public void service(ServiceManager smanager) throws ServiceException 071 { 072 super.service(smanager); 073 _manager = smanager; 074 _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE); 075 _skinModelsManager = (SkinModelsManager) smanager.lookup(SkinModelsManager.ROLE); 076 } 077 078 private void _configure() throws ConfigurationException 079 { 080 _lastConfigureTime = System.currentTimeMillis(); 081 082 DefaultConfiguration newConf = new DefaultConfiguration("i18n"); 083 084 Configuration cataloguesConf = _conf.getChild("catalogues", false); 085 DefaultConfiguration catalogues = new DefaultConfiguration("catalogues"); 086 newConf.addChild(catalogues); 087 088 if (cataloguesConf != null) 089 { 090 catalogues.addAll(cataloguesConf); 091 } 092 093 _configureSkinsCatalogue(catalogues); 094 _configureModelsCatalogue(catalogues); 095 096 super.configure(newConf); 097 } 098 099 private void _configureSkinsCatalogue(DefaultConfiguration catalogues) 100 { 101 for (String skinId : _skinsManager.getSkins()) 102 { 103 String name = skinId; 104 String id = "skin." + name; 105 106 DefaultConfiguration catalogue = new DefaultConfiguration("catalogue"); 107 catalogue.setAttribute("id", id); 108 catalogue.setAttribute("name", "messages"); 109 110 Skin skin = _skinsManager.getSkin(skinId); 111 for (Skin skinOrParentSkin : _skinsManager.getSkinAndParents(skin)) 112 { 113 DefaultConfiguration location = new DefaultConfiguration("location"); 114 location.setValue("skin-raw:" + skinOrParentSkin.getId() + "://i18n"); 115 catalogue.addChild(location); 116 } 117 118 catalogues.addChild(catalogue); 119 } 120 } 121 122 123 private void _configureModelsCatalogue(DefaultConfiguration catalogues) 124 { 125 for (String modelId : _skinModelsManager.getModels()) 126 { 127 String name = modelId; 128 String id = "model." + name; 129 130 DefaultConfiguration catalogue = new DefaultConfiguration("catalogue"); 131 catalogue.setAttribute("id", id); 132 catalogue.setAttribute("name", "messages"); 133 catalogue.setAttribute("location", "model:" + modelId + "://i18n"); 134 135 catalogues.addChild(catalogue); 136 } 137 } 138 139 @Override 140 protected ParamSaxBuffer getMessage(String catalogueID, String key) 141 { 142 Request request = ObjectModelHelper.getRequest(objectModel); 143 144 String skinLocation = (String) request.getAttribute("skin-location"); 145 if (skinLocation != null && catalogueID != null && catalogueID.startsWith("skin")) 146 { 147 ParamSaxBuffer message = null; 148 149 String skinName = _skinsManager.getSkinNameFromRequest(request); 150 151 if (StringUtils.isNotEmpty(skinName)) 152 { 153 message = _getExternalMessage("ametys-home://" + skinLocation + "/" + skinName + "/i18n", key); 154 } 155 156 if (message == null) 157 { 158 message = getUntranslatedMessage(catalogueID, key); 159 } 160 161 return message; 162 } 163 else 164 { 165 return super.getMessage(catalogueID, key); 166 } 167 } 168 169 private ParamSaxBuffer _getExternalMessage (String externalLocation, String key) 170 { 171 try 172 { 173 XMLResourceBundle bundle = (XMLResourceBundle) factory.select(externalLocation, "messages", locale); 174 if (bundle == null) 175 { 176 // Can't translate 177 if (getLogger().isDebugEnabled()) 178 { 179 getLogger().debug("No bundle found for catalogue location '" + externalLocation + "'"); 180 } 181 return null; 182 } 183 184 try 185 { 186 return (ParamSaxBuffer) bundle.getObject(key); 187 } 188 catch (MissingResourceException e) 189 { 190 if (getLogger().isDebugEnabled()) 191 { 192 getLogger().debug("Untranslated key: '" + key + "'"); 193 } 194 } 195 } 196 catch (ComponentException e) 197 { 198 if (getLogger().isDebugEnabled()) 199 { 200 getLogger().debug("Untranslated key: '" + key + "'"); 201 } 202 } 203 204 return null; 205 206 } 207 208 /** 209 * Call this method when you change the list of i18n catalogues <br> by adding a new skin for example. 210 */ 211 public static void needsReload() 212 { 213 _needsReloadTime = System.currentTimeMillis(); 214 } 215 216 @Override 217 public void setup(SourceResolver resolver, Map objModel, String source, Parameters parameters) throws ProcessingException, SAXException, IOException 218 { 219 try 220 { 221 if (_needsReloadTime > _lastConfigureTime) 222 { 223 _configure(); 224 } 225 } 226 catch (ConfigurationException e) 227 { 228 String errorMessage = "Error with i18n on work dir"; 229 getLogger().error(errorMessage, e); 230 throw new SAXException(errorMessage, e); 231 } 232 233 super.setup(resolver, objModel, source, parameters); 234 } 235}