001/* 002 * Copyright 2020 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.plugins.mobileapp.action; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.IOException; 021import java.net.MalformedURLException; 022import java.nio.charset.StandardCharsets; 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.avalon.framework.parameters.Parameters; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.cocoon.acting.ServiceableAction; 032import org.apache.cocoon.environment.ObjectModelHelper; 033import org.apache.cocoon.environment.Redirector; 034import org.apache.cocoon.environment.Request; 035import org.apache.cocoon.environment.SourceResolver; 036import org.apache.cocoon.environment.http.HttpResponse; 037import org.apache.commons.io.FileUtils; 038import org.apache.excalibur.source.Source; 039import org.apache.excalibur.source.impl.FileSource; 040 041import org.ametys.core.cocoon.JSonReader; 042import org.ametys.core.util.JSONUtils; 043import org.ametys.runtime.config.Config; 044import org.ametys.web.repository.site.Site; 045import org.ametys.web.repository.site.SiteManager; 046 047import com.google.gson.JsonParseException; 048 049/** 050 * Returns the theme for a site 051 */ 052public class GetThemeAction extends ServiceableAction 053{ 054 /** global configuration about projects enabled/disabled */ 055 protected static final String __PROJECT_ENABLED_CONF_ID = "plugin.mobileapp.project.enabled"; 056 057 /** Path of the base directory for mobileapp conf */ 058 protected static final String BASE_DIR = "context://WEB-INF/param/mobileapp/"; 059 060 /** Path of the theme.json file */ 061 public static final String THEME_JSON = BASE_DIR + "theme.json"; 062 063 /** Authentication Token Manager */ 064 protected SiteManager _siteManager; 065 066 /** Source Resolver */ 067 protected org.apache.excalibur.source.SourceResolver _sourceResolver; 068 069 /** JSON Utils */ 070 protected JSONUtils _jsonUtils; 071 072 @Override 073 public void service(ServiceManager smanager) throws ServiceException 074 { 075 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 076 _sourceResolver = (org.apache.excalibur.source.SourceResolver) smanager.lookup(org.apache.excalibur.source.SourceResolver.ROLE); 077 _jsonUtils = (JSONUtils) smanager.lookup(JSONUtils.ROLE); 078 } 079 080 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 081 { 082 Map<String, Object> result = new HashMap<>(); 083 Request request = ObjectModelHelper.getRequest(objectModel); 084 085 String siteName = parameters.getParameter("site"); 086 Site site = _siteManager.getSite(siteName); 087 088 if (site != null) 089 { 090 try 091 { 092 Map<String, Object> jsonMap = parseJson(); 093 result = transformMap(jsonMap, site); 094 // Icons first, this is where it can fail 095 096 result.put("code", 200); 097 result.put("name", site.getTitle()); 098 result.put("main_link", site.getUrl()); 099 100 boolean projectsEnabled = Config.getInstance().getValue(__PROJECT_ENABLED_CONF_ID); 101 result.put("project_enabled", projectsEnabled); 102 } 103 catch (JsonParseException e) 104 { 105 getLogger().error("Invalid JSON file for " + THEME_JSON, e); 106 result = new HashMap<>(); 107 result.put("code", 500); 108 result.put("message", "invalid-icons-json"); 109 HttpResponse response = (HttpResponse) ObjectModelHelper.getResponse(objectModel); 110 response.setStatus(500); 111 } 112 } 113 else 114 { 115 result.put("code", 500); 116 result.put("message", "site-not-found"); 117 HttpResponse response = (HttpResponse) ObjectModelHelper.getResponse(objectModel); 118 response.setStatus(500); 119 } 120 121 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 122 return EMPTY_MAP; 123 } 124 125 /** 126 * Get the content of the json file as a map 127 * @return the map representing the json file 128 * @throws IOException impossible to read the json file 129 */ 130 protected Map<String, Object> parseJson() throws IOException 131 { 132 Source src = _sourceResolver.resolveURI(THEME_JSON); 133 134 File file = ((FileSource) src).getFile(); 135 136 if (!file.exists()) 137 { 138 throw new JsonParseException("The file " + THEME_JSON + " does not exist."); 139 } 140 141 try (FileInputStream is = FileUtils.openInputStream(file)) 142 { 143 String body = new String(is.readAllBytes(), StandardCharsets.UTF_8); 144 Map<String, Object> jsonMap = _jsonUtils.convertJsonToMap(body); 145 return jsonMap; 146 } 147 catch (IllegalArgumentException e) 148 { 149 throw new JsonParseException("The file " + THEME_JSON + " must be a valid json containing a map with list of maps, at least : {\"icons\":[{\"src\":\"resources/img/logo96x96.png\"}]}."); 150 } 151 } 152 153 /** 154 * Parse the input json, and transform all String called src to be relative to the site and skin used. 155 * @param jsonMap the map read from the json file 156 * @param site site to use to get it's url and skin 157 * @return the json map with src modified 158 * @throws MalformedURLException impossible to find the json file 159 * @throws IOException impossible to read the json file 160 * @throws JsonParseException json file inexistant or invalid 161 */ 162 protected Map<String, Object> transformMap(Map<String, Object> jsonMap, Site site) throws MalformedURLException, IOException, JsonParseException 163 { 164 Map<String, Object> result = new HashMap<>(); 165 166 for (String key : jsonMap.keySet()) 167 { 168 Object object = jsonMap.get(key); 169 Object transformedObject = transformObject(key, object, site); 170 result.put(key, transformedObject); 171 } 172 173 return result; 174 } 175 176 /** 177 * Parse the input json, and transform all String called src to be relative to the site and skin used. 178 * @param jsonList a list in the json file 179 * @param site site to use to get it's url and skin 180 * @return the json map with src modified 181 * @throws MalformedURLException impossible to find the json file 182 * @throws IOException impossible to read the json file 183 * @throws JsonParseException json file inexistant or invalid 184 */ 185 protected List<Object> transformList(List<Object> jsonList, Site site) throws MalformedURLException, IOException, JsonParseException 186 { 187 List<Object> result = new ArrayList<>(); 188 for (Object object : jsonList) 189 { 190 Object transformedObject = transformObject(null, object, site); 191 result.add(transformedObject); 192 } 193 return result; 194 } 195 196 /** 197 * Parse the input json, and transform all String called src to be relative to the site and skin used. 198 * @param key in case the item was in a map, the key associated (only src will be translated) 199 * @param object the object to read from the json 200 * @param site site to use to get it's url and skin 201 * @return the json map with src modified 202 * @throws MalformedURLException impossible to find the json file 203 * @throws IOException impossible to read the json file 204 * @throws JsonParseException json file inexistant or invalid 205 */ 206 protected Object transformObject(String key, Object object, Site site) throws MalformedURLException, IOException, JsonParseException 207 { 208 Object result = object; 209 if (object instanceof String) 210 { 211 String value = (String) object; 212 if ("src".equalsIgnoreCase(key)) 213 { 214 result = getImageUrl(value, site); 215 } 216 } 217 else if (object instanceof List) 218 { 219 @SuppressWarnings("unchecked") 220 List<Object> list = (List<Object>) object; 221 result = transformList(list, site); 222 } 223 else if (object instanceof Map) 224 { 225 @SuppressWarnings("unchecked") 226 Map<String, Object> map = (Map<String, Object>) object; 227 result = transformMap(map, site); 228 } 229 230 return result; 231 } 232 233 /** 234 * Get thu full url of the theme image, based on the site url and used skin 235 * @param relativePath path of the image in the skin 236 * @param site site used 237 * @return the image full url 238 */ 239 protected String getImageUrl(String relativePath, Site site) 240 { 241 String url = site.getUrl() 242 + "/skins/" 243 + site.getSkinId() 244 + "/" 245 + relativePath; 246 247 return url; 248 } 249}