001/* 002 * Copyright 2017 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.linkdirectory.link; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.avalon.framework.parameters.Parameters; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.cocoon.acting.ServiceableAction; 029import org.apache.cocoon.environment.ObjectModelHelper; 030import org.apache.cocoon.environment.Redirector; 031import org.apache.cocoon.environment.Request; 032import org.apache.cocoon.environment.Response; 033import org.apache.cocoon.environment.SourceResolver; 034import org.apache.commons.lang3.StringUtils; 035import org.apache.http.HttpEntity; 036import org.apache.http.client.config.RequestConfig; 037import org.apache.http.client.methods.CloseableHttpResponse; 038import org.apache.http.client.methods.HttpGet; 039import org.apache.http.entity.ContentType; 040import org.apache.http.impl.client.CloseableHttpClient; 041import org.apache.http.impl.client.HttpClientBuilder; 042 043import org.ametys.core.cocoon.JSonReader; 044import org.ametys.core.upload.Upload; 045import org.ametys.core.upload.UploadManager; 046import org.ametys.core.user.CurrentUserProvider; 047import org.ametys.core.user.UserIdentity; 048import org.ametys.core.util.JSONUtils; 049import org.ametys.plugins.linkdirectory.DirectoryHelper; 050import org.ametys.plugins.linkdirectory.Link; 051import org.ametys.plugins.linkdirectory.Link.LinkType; 052import org.ametys.plugins.repository.RepositoryConstants; 053import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 054import org.ametys.web.repository.site.SiteManager; 055import org.ametys.web.url.UrlPreviewComponent; 056 057/** 058 * Adds a new user link in Ametys. 059 */ 060public class AddUserLinkAction extends ServiceableAction 061{ 062 private static int _ICO_REQUEST_TIMEOUT = 3000; 063 064 /** The DAO for {@link Link}s */ 065 protected LinkDAO _linkDAO; 066 /** The current user provider */ 067 protected CurrentUserProvider _currentUserProvider; 068 /** The directory helper */ 069 protected DirectoryHelper _directoryHelper; 070 /** The site manager */ 071 protected SiteManager _siteManager; 072 /** The url preview component */ 073 protected UrlPreviewComponent _urlPreviewComponent; 074 /** The upload manager */ 075 protected UploadManager _uploadManager; 076 /** The JSON utils */ 077 protected JSONUtils _jsonUtils; 078 079 @Override 080 public void service(ServiceManager smanager) throws ServiceException 081 { 082 super.service(smanager); 083 _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 084 _linkDAO = (LinkDAO) smanager.lookup(LinkDAO.ROLE); 085 _directoryHelper = (DirectoryHelper) smanager.lookup(DirectoryHelper.ROLE); 086 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 087 _urlPreviewComponent = (UrlPreviewComponent) smanager.lookup(UrlPreviewComponent.ROLE); 088 _uploadManager = (UploadManager) smanager.lookup(UploadManager.ROLE); 089 _jsonUtils = (JSONUtils) smanager.lookup(JSONUtils.ROLE); 090 } 091 092 @Override 093 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 094 { 095 Request request = ObjectModelHelper.getRequest(objectModel); 096 Map<String, Object> result = new HashMap<>(); 097 098 UserIdentity user = _currentUserProvider.getUser(); 099 100 // Check user authenticated 101 if (user != null) 102 { 103 // Create link 104 Map<String, Object> createParameters = new HashMap<>(); 105 String siteName = request.getParameter("siteName"); 106 String lang = request.getParameter("lang"); 107 String url = request.getParameter("url"); 108 //The color key, default color will be used if it is not present or empty 109 String color = request.getParameter("color"); 110 111 createParameters.put("siteName", siteName); 112 createParameters.put("lang", lang); 113 createParameters.put("url-type", LinkType.URL.toString()); 114 createParameters.put("url", url); 115 createParameters.put("title", request.getParameter("title")); 116 createParameters.put("color", color); 117 118 String userAgent = request.getHeader("User-Agent"); 119 Upload upload = _getLinkFavicon(url, user, userAgent); 120 if (upload != null) 121 { 122 Map<String, Object> linkMap = new HashMap<>(); 123 linkMap.put("id", upload.getId()); 124 linkMap.put("type", "external"); 125 createParameters.put("picture", _jsonUtils.convertObjectToJson(linkMap)); 126 } 127 128 _handleTheme(request, createParameters, siteName, lang); 129 130 Map<String, Object> createdLinkresult = _createUserLink(request, createParameters); 131 132 if (createdLinkresult.containsKey("already-exists")) 133 { 134 result.put("error", "already-exists"); 135 } 136 else 137 { 138 result.put("error", "none"); 139 result.putAll(createdLinkresult); 140 } 141 } 142 else 143 { 144 result.put("error", "unauthenticated-user"); 145 } 146 147 Response response = ObjectModelHelper.getResponse(objectModel); 148 response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin")); 149 response.setHeader("Access-Control-Allow-Credentials", "true"); 150 151 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 152 return EMPTY_MAP; 153 } 154 155 private void _handleTheme(Request request, Map<String, Object> createParameters, String siteName, String lang) 156 { 157 String themeAsString = request.getParameter("themes"); 158 159 List<String> themes = new ArrayList<>(); 160 // We have a theme name ... 161 if (StringUtils.isNotBlank(themeAsString)) 162 { 163 // ... retrieve JCR id from theme name 164 String themeId = _directoryHelper.getThemeIdFromThemeName(_siteManager.getSite(siteName), lang, themeAsString); 165 if (StringUtils.isNotBlank(themeId)) 166 { 167 themes.add(themeId); 168 } 169 } 170 // if not, just send an empty list for the user link creation 171 createParameters.put("themes", themes); 172 } 173 174 private Map<String, Object> _createUserLink(Request request, Map<String, Object> parameters) 175 { 176 // Retrieve the current workspace. 177 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 178 179 try 180 { 181 // Force the workspace. 182 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE); 183 184 return _linkDAO.createUserLink(parameters); 185 } 186 finally 187 { 188 // Restore context 189 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 190 } 191 } 192 193 /** 194 * Set the favicon of the link url 195 * @param url the url of the favicon 196 * @param user the user 197 * @param userAgent The user agent 198 * @return the favicon upload map 199 */ 200 protected Upload _getLinkFavicon(String url, UserIdentity user, String userAgent) 201 { 202 Upload upload = null; 203 try 204 { 205 String favIconPath = _urlPreviewComponent.getFavicon(url); 206 String favIconName = StringUtils.substringAfterLast(favIconPath, "/"); 207 if (StringUtils.isNotBlank(favIconPath)) 208 { 209 RequestConfig requestConfig = RequestConfig.custom() 210 .setConnectTimeout(_ICO_REQUEST_TIMEOUT) 211 .setConnectionRequestTimeout(_ICO_REQUEST_TIMEOUT) 212 .setSocketTimeout(_ICO_REQUEST_TIMEOUT) 213 .build(); 214 215 try (CloseableHttpClient httpclient = HttpClientBuilder.create() 216 .setDefaultRequestConfig(requestConfig) 217 .useSystemProperties() 218 .setUserAgent(userAgent) 219 .build()) 220 { 221 // Prepare a request object 222 HttpGet get = new HttpGet(favIconPath); 223 try (CloseableHttpResponse httpResponse = httpclient.execute(get)) 224 { 225 int statusCode = httpResponse.getStatusLine().getStatusCode(); 226 if (statusCode != 200) 227 { 228 getLogger().warn("Error " + statusCode + ". Can't set the favicon with url " + favIconPath + " to the link " + url); 229 return upload; 230 } 231 232 try (InputStream is = httpResponse.getEntity().getContent()) 233 { 234 HttpEntity entity = httpResponse.getEntity(); 235 String mimeType = ContentType.get(entity).getMimeType(); 236 if (mimeType.equals("image/x-icon") || mimeType.equals("image/vnd.microsoft.icon")) 237 { 238 // If the image is .ico, convert it in .png 239 InputStream convertedIcoToPngIS = _urlPreviewComponent.convertIcoToPng(is); 240 upload = _uploadManager.storeUpload(user, StringUtils.replace(favIconName, ".ico", ".png"), convertedIcoToPngIS); 241 } 242 else 243 { 244 upload = _uploadManager.storeUpload(user, favIconName, is); 245 } 246 } 247 } 248 } 249 catch (IOException e) 250 { 251 getLogger().warn("Unable to download favicon from url " + favIconPath + " to the link " + url, e); 252 } 253 } 254 } 255 catch (Exception e) 256 { 257 getLogger().warn("Unable to get favicon from link url " + url, e); 258 } 259 260 return upload; 261 } 262}