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