All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yadaframework.components.YadaWebUtil Maven / Gradle / Ivy

There is a newer version: 0.7.7.R4
Show newest version
package net.yadaframework.components;

import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_AUTOCLOSE;
import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_BODY;
import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_CALLSCRIPT;
import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_REDIRECT;
import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_RELOADONCLOSE;
import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_SEVERITY;
import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_TITLE;
import static net.yadaframework.core.YadaConstants.KEY_NOTIFICATION_TOTALSEVERITY;
import static net.yadaframework.core.YadaConstants.VAL_NOTIFICATION_SEVERITY_ERROR;
import static net.yadaframework.core.YadaConstants.VAL_NOTIFICATION_SEVERITY_INFO;
import static net.yadaframework.core.YadaConstants.VAL_NOTIFICATION_SEVERITY_OK;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Entities.EscapeMode;
import org.jsoup.safety.Cleaner;
import org.jsoup.safety.Safelist;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StreamUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.util.UriComponentsBuilder;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.yadaframework.core.YadaConfiguration;
import net.yadaframework.core.YadaConstants;
import net.yadaframework.core.YadaLocalEnum;
import net.yadaframework.exceptions.YadaInvalidUsageException;
import net.yadaframework.exceptions.YadaSystemException;
import net.yadaframework.web.YadaPageRequest;
import net.yadaframework.web.YadaPageRows;

@Lazy // Lazy because used in YadaCmsConfiguration, and it woud give a circular refecence exception otherwise
@Service
public class YadaWebUtil {
	private final transient Logger log = LoggerFactory.getLogger(getClass());

	@Autowired private YadaConfiguration config;
	@Autowired private YadaUtil yadaUtil;
	@Autowired private MessageSource messageSource;
	
	public final YadaPageRequest FIND_ONE = YadaPageRequest.of(0, 1);

	// Characters that should never be found or placed in a slug
	private static final String PATTERN_INVALID_SLUG = "[?%:,;=&!+~()@*$'\"\\s]";

	private Map> sortedLocalEnumCache = new HashMap<>();
	
	/**
	 * Returns true if the MultipartFile has not been uploaded at all, not even an empty file
	 * @param multipartFile
	 */
	public boolean isMultipartMissing(MultipartFile multipartFile) {
		// It's not enough to check for null or isEmpty() or size() because an empty file wouldn't be accepted.
		// The only condition when no file has been uploaded at all is that the original filename is missing.
		return multipartFile==null || StringUtils.isEmpty(multipartFile.getOriginalFilename());
	}
	
	/**
	 * Creates a dynamic @RequestMapping i.e. there's no need for it to be annotated and located in a controller instance.
	 * Useful if the url can be user-defined via some cms for example.
	 * @param newPath some url that the code will handle, like "/mypage"
	 * @param controllerInstance the instance of a class that will handle the page. It can be a @Controller but it's not required.
	 * @param methodName the name of the method that will handle the page. The method signature must be (Model, Locale).
	 * @param requestMappingHandlerMapping retrieve this via autowiring because it can't be autowired in YadaWebUtil directly
	 */
	public void registerDynamicMapping(String newPath, Object controllerInstance, String methodName, RequestMappingHandlerMapping requestMappingHandlerMapping) {
        try {
            Method method = controllerInstance.getClass().getMethod(methodName, Model.class, Locale.class);

            RequestMappingInfo requestMappingInfo = RequestMappingInfo
                .paths(newPath)
                // There could be many conditions here
                // .methods(RequestMethod.GET)
                // .produces(MediaType.APPLICATION_JSON_VALUE)
                .build();
            requestMappingHandlerMapping.registerMapping(requestMappingInfo, controllerInstance, method);
        } catch (Exception e) {
        	throw new YadaInvalidUsageException("Can't create dynamic mapping {} for {}.{}(model, locale)", newPath, controllerInstance.getClass(), methodName, e);
        }
	}
	
	/**
	 * Returns the application-relative url of a file from the contents folder
	 * @param someContentFile some file that is inside the contents folder at any depth
	 * @return a string like "/contents/path/to/file.gif"
	 */
	public String getContentUrlRelative(File someContentFile) {
		return "/" + config.getContentName() + "/" + yadaUtil.relativize(config.getContentsFolder(), someContentFile);
	}
		
	/**
	 * Set a cookie
	 * @param cookieName
	 * @param cookieValue
	 * @param expirationDays can be 0 to delete the cookie or <0 to persist until browser shutdown
	 * @param response
	 */
	public void setCookie(String cookieName, String cookieValue, int expirationDays, HttpServletResponse response) {
	    Cookie cookie = new Cookie(cookieName, cookieValue);
	    cookie.setMaxAge(60*60*expirationDays);
	    response.addCookie(cookie);
	}
	
	/**
	 * Returns the value of a cookie, null if not defined
	 * @param request
	 * @param cookieName
	 */
	public String getCookieValue(HttpServletRequest request, String cookieName) {
	    Cookie[] cookies = request.getCookies();
	    if (cookies != null) {
	        for (Cookie cookie : cookies) {
	            if (cookieName.equals(cookie.getName())) {
	                return cookie.getValue();
	            }
	        }
	    }
	    return null;
	}

	/**
	 * Save to disk a base64 image received from javascript.
	 * @param base64Image the image string in the format data:image/png;base64,xxxxxxxxx where image/png can be any image format and xxxxxxxxx is the encoded image
	 * @param targetFileNoExtension the target file without extension: the extension is taken from base64Image
	 * @throws YadaInvalidUsageException if the image can't be decoded
	 * @throws YadaSystemException if saving fails
	 * @return the saved file (with extension)
	 */
	public File saveBase64Image(String base64Image, File targetFileNoExtension) {
		String extension;
		byte[] decodedImg;
		try {
			String[] parts = base64Image.split(",");
			extension = parts[0].split("/")[1].split(";")[0];
			String imageString = parts[1];
			decodedImg = Base64.getDecoder().decode(imageString.getBytes());
		} catch (Exception e) {
			throw new YadaInvalidUsageException("Can't save base64Image that starts with \"{}\" to {}", base64Image.substring(0, 30), targetFileNoExtension, e);
		}
		File parentFolder = targetFileNoExtension.getParentFile();
		if (!parentFolder.exists()) {
			parentFolder.mkdirs();
        }
        File targetFile = new File(parentFolder, targetFileNoExtension.getName() + "." + extension);
        try (FileOutputStream fos = new FileOutputStream(targetFile)) {
            fos.write(decodedImg);
        } catch (IOException e) {
            throw new YadaSystemException("Can't save base64Image to {}", targetFileNoExtension, e);
        }
        return targetFile;
    }

	/**
	 * Perform autowiring of an instance that doesn't come from the Spring context, e.g. a JPA @Entity or normal java instance made with new.
	 * Post processing (@PostConstruct etc) and initialization are also performed.
	 * This version can inject beans from the WebApplicationContext like @Controllers. You may consider a class hierarchy redesign before using this method.
	 * @param instance to autowire
	 * @return the autowired/initialized bean instance, either the original or a wrapped one
	 * @see {@link YadaUtil#autowireAndInitialize(Object)}, {@link AutowireCapableBeanFactory#autowireBean(Object)}, {@link AutowireCapableBeanFactory#initializeBean(Object, String)}, {@link #autowire(Object)}
	 */
	public Object autowireAndInitialize(Object instance) {
		HttpServletRequest request = getCurrentRequest();
		if (request!=null) {
			log.debug("Using YadaWebUtil.autowireAndInitialize(): please use YadaUtil.autowireAndInitialize() instead "
				+ "and, if the bean is not found there, you should consider a redesign. No technical problem in going ahead here though.");
			String attrName = FrameworkServlet.SERVLET_CONTEXT_PREFIX + AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME;
			ServletContext servletContext = request.getServletContext();
			WebApplicationContext webApplicationContext = (WebApplicationContext) servletContext.getAttribute(attrName);
			// WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
			AutowireCapableBeanFactory autowireCapableBeanFactory = webApplicationContext.getAutowireCapableBeanFactory();
			autowireCapableBeanFactory.autowireBean(instance);
			return autowireCapableBeanFactory.initializeBean(instance, instance.getClass().getSimpleName());
		}
		log.error("No HttpServletRequest in current thread");
		return null;
	}
	
	public boolean isEmpty(YadaPageRows yadaPageRows) {
		return yadaPageRows==null || yadaPageRows.isEmpty();
	}

	/**
	 * Returns the last part of the current request, for example from "/some/product" returns "product"
	 * @param request
	 * @return
	 * @see #getRequestMapping()
	 */
	public String getRequestMapping(HttpServletRequest request) {
		String path = request.getServletPath();
		String[] parts = path.split("/");
		if (parts.length==0) {
			return "/";
		}
		return parts[parts.length-1];
	}
	
	/**
	 * Returns the last part of the current request, for example from "/some/product" returns "product"
	 * This version does not require the request to be passed as parameter.
	 * @return
	 * @see #getRequestMapping(HttpServletRequest)
	 */
	public String getRequestMapping() {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        UriComponentsBuilder ucb = UriComponentsBuilder.fromPath(sra.getRequest().getRequestURI());
        return ucb.build().getPathSegments().get(ucb.build().getPathSegments().size()-1);
	}

	/**
	 * If the url is a full url that points to our server, make it relative to the server and strip any language in the path.
	 * The result can be used in thymeleaf @{url} statements and will get the proper browser language when needed.
	 * @param fullUrlWithHttp something like "https://my.site.com/en/something/here", can be empty or null
	 * @param request
	 * @return someting like "/something/here", or null
	 */
	public String removeLanguageFromOurUrl(String fullUrlWithHttp, HttpServletRequest request) {
		String url = StringUtils.trimToNull(fullUrlWithHttp); // "https://my.site.com/en/something/here"
		if (url==null) {
			return null;
		}
		String ourAddress = this.getWebappAddress(request); // "https://my.site.com"
		if (url.startsWith(ourAddress)) {
			url = url.substring(ourAddress.length()); // "/en/something/here"
			if (config.isLocalePathVariableEnabled() && url.length()>=3) {
				// If the url starts with a configured language, strip it
				List localeStrings = config.getLocaleStrings();
				for (String language : localeStrings) {
					if (url.startsWith("/" + language + "/")) {
						url = url.substring(language.length()+1); // "/something/here"
						break;
					}
				}
			}
		}
		return url;
	}

	/**
	 * Adds all request parameters to the Model, optionally filtering by name.
	 * Existing model attributes are not overwritten.
	 * @param model
	 * @param request
	 * @param nameFilter parameter names that should pass through to the Model
	 */
	public void passThrough(Model model, HttpServletRequest request, String ... nameFilter) {
		Map parameterMap = request.getParameterMap();
		Set nameFilterSet = new HashSet<>();
		nameFilterSet.addAll(Arrays.asList(nameFilter));

		for (Map.Entry param : parameterMap.entrySet()) {
			String key = param.getKey();
			if (nameFilterSet.isEmpty() || nameFilterSet.contains(key)) {
				String[] valueArray = param.getValue();
				Object value = valueArray.length==1?valueArray[0]:valueArray;
				if (value!=null && !model.containsAttribute(key)) {
					model.addAttribute(key, value);
				}
			}
		}
	}

	/**
	 * Add a url parameter or change its value if present
	 * @param sourceUrl a full or relative url. Can also be just the query string starting with "?"
	 * @param paramName the name of the parameter, not urlencoded
	 * @param paramValue the value of the parameter, not urlencoded. Can be null to only have the paramName in the url
	 * @return
	 */
	// Not tested yet
	public String addOrUpdateUrlParameter(String sourceUrl, String paramName, String paramValue) {
		String encodedParamName = urlEncode(paramName);
		String encodedParamValue = urlEncode(paramValue); // Can be null
		String equalsAndValue = encodedParamValue==null? "" : "=" + encodedParamValue;
		StringBuilder result = new StringBuilder();
		int queryPos = sourceUrl.indexOf("?");
		boolean found=false;
		if (queryPos<0) {
			// There is no query string yet
			result.append(sourceUrl);
		} else if (queryPos>0) {
			// There is a query string already
			result.append(sourceUrl.substring(0, queryPos));
		}
		result.append("?");
		if (queryPos>-1) {
			// Check existing parameters
			String query = sourceUrl.substring(queryPos); // "?xxx=yyy&zzz"
			String[] params = query.split("[?&]"); // ["xxx=yyy", "zzz"]
			for (int i = 0; i < params.length; i++) {
				String[] parts = params[i].split("=");
				String name = parts[0];
				if (name.equals(encodedParamName)) {
					result.append(encodedParamName).append(equalsAndValue);
					found = true;
				} else {
					result.append(params[i]);
				}
				if (i0) {
			boolean isStart = true;
			int questionPos = result.indexOf("?");
			if (questionPos<0) {
				result.append("?");
			} else {
				if (url.length()>questionPos+1) {
					// There is some parameter already
					isStart = false;
				}
			}
			boolean isName = true;
			String lastName = null;
			for (String param : params) {
				if (isName && !isStart) {
					result.append("&");
				}
				if (isName) { // name
					// null names are skipped together with their value
					if (param!=null) {
						result.append(param);
						result.append("=");
					}
					lastName = param;
				} else { // value
					if (lastName!=null) {
						// Null values remain empty
						if (param!=null) {
							result.append(param);
						}
					} else {
						log.debug("Skipping null name and its value '{}'", param);
					}
				}
				isStart=false;
				isName=!isName;
			}
		}
		return result.toString();
	}

	/**
	 * Make a zip file and send it to the client. The temp file is automatically deleted.
	 * @param returnedFilename the name of the file to create and send, with extension. E.g.: data.zip
	 * @param sourceFiles the files to zip
	 * @param filenamesNoExtension the name of each file in the zip - null to keep the original names
	 * @param ignoreErrors true to ignore errors when adding a file and keep going, false for an exception when a file can't be zipped
	 * @param response
	 */
	public void downloadZip(String returnedFilename, File[] sourceFiles, String[] filenamesNoExtension, boolean ignoreErrors, HttpServletResponse response) throws Exception {
		File zipFile = null;
		try {
			zipFile = File.createTempFile("downloadZip", null);
			yadaUtil.createZipFile(zipFile, sourceFiles, filenamesNoExtension, ignoreErrors);
			response.setContentType("application/zip");
			response.setHeader("Content-disposition", "attachment; filename=" + returnedFilename);
			try (OutputStream out = response.getOutputStream(); FileInputStream in = new FileInputStream(zipFile)) {
				IOUtils.copy(in,out);
			}
		} finally {
			if (zipFile!=null) {
				zipFile.delete();
			}
		}

	}

	/**
	 * Sorts a localized enum according to the locale specified
	 * @param localEnum the class of the enum, e.g. net.yadaframework.persistence.entity.YadaJobState.class
	 * @param locale
	 * @return a list of sorted enums of the given class
	 */
	public > List sortLocalEnum(Class localEnum, Locale locale) {
		String key = localEnum.getName() + "." + locale.toString();
		@SuppressWarnings("unchecked")
		List result = (List) sortedLocalEnumCache.get(key);
		if (result==null) {
			T[] enums = localEnum.getEnumConstants();
			Arrays.sort(enums, new Comparator() {
				@Override
				public int compare(T o1, T o2) {
					String v1 = o1.toString(messageSource, locale);
					String v2 = o2.toString(messageSource, locale);
					return v1.compareTo(v2);
				}
			});
			result = Arrays.asList(enums);
			sortedLocalEnumCache.put(key, result);
		}
		return result;
	}

	/**
	 * Returns the first language in the request language header as a string.
	 * @return the language string, like "en_US", or "" if not found
	 */
	public String getBrowserLanguage() {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String languageHeader = StringUtils.trimToEmpty(request.getHeader("Accept-Language")); // en-US,en-GB;q=0.9,en;q=0.8,it;q=0.7,es;q=0.6,la;q=0.5
		int pos = languageHeader.indexOf(',');
		if (pos>4) {
			try {
				return languageHeader.substring(0, pos);
			} catch (Exception e) {
				// Invalid header - ignored
			}
		}
		return "";
	}

	/**
	 * Returns the first country in the request language header as a string.
	 * @return the country string, like "US", or "" if not found
	 */
	public String getBrowserCountry() {
		String browserLanguage = getBrowserLanguage();
		int pos = browserLanguage.indexOf('-');
		if (pos>1) {
			try {
				return browserLanguage.substring(pos+1);
			} catch (Exception e) {
				// Invalid header - ignored
			}
		}
		return "";
	}

	/**
	 * Save an uploaded file to a temporary file
	 * @param attachment
	 * @return the temporary file holding the uploaded file, or null if no file has bee attached
	 * @throws IOException, IllegalStateException
	 */
	public File saveAttachment(MultipartFile attachment) throws IOException {
		if (!isMultipartMissing(attachment)) {
			File targetFile = File.createTempFile("upload-", null);
			saveAttachment(attachment, targetFile);
			return targetFile;
		}
		return null;
	}

	/**
	 * Save an uploaded file to the given target file
	 * @param attachment
	 * @param targetPath
	 * @throws IOException, IllegalStateException
	 */
	public void saveAttachment(MultipartFile attachment, Path targetPath) throws IOException {
		saveAttachment(attachment, targetPath.toFile());
		//		try (InputStream inputStream = attachment.getInputStream(); OutputStream outputStream = new FileOutputStream(targetPath.toFile())) {
		//			IOUtils.copy(inputStream, outputStream);
		//		}
	}

	/**
	 * Save an uploaded file to the given target file
	 * @param attachment
	 * @param targetFile
	 * @throws IOException, IllegalStateException
	 */
	public void saveAttachment(MultipartFile attachment, File targetFile) throws IOException {
		attachment.transferTo(targetFile);
		//
		//		try (InputStream inputStream = attachment.getInputStream(); OutputStream outputStream = new FileOutputStream(targetFile)) {
		//			IOUtils.copy(inputStream, outputStream);
		//		}
	}

	/**
	 * Fix a url so that it valid and doesn't allow XSS attacks
	 * @see https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
	 * @param url some text typed by the user
	 * @return the same url or null if blank or a fixed one that may or may not work as expected, but won't pose a security risk
	 */
	public String sanitizeUrl(String url) {
		if (StringUtils.isBlank(url)) {
			return null;
		}
		url = url.replaceAll("[^-A-Za-z0-9+&@#/%?=~_|!:,.;\\(\\)]", "");
		if (!url.startsWith("http://") && !url.startsWith("https://")) {
			url = "http://" + url;
		}
		return url;
	}

	/**
	 * Assembles a url given its parts as string.
	 * @param segments the initial parts of the url up to the query string. Leading and trailing slashes are added when missing.
	 * URLEncoding is not performed.
	 * @return
	 * @see #makeUrl(String[], Map, Boolean)
	 */
	public String makeUrl(String...segments) {
		return makeUrl(segments, null, null);
	}

	/**
	 * Assembles a url given its parts as string.
	 * @param segments the initial parts of the url up to the query string. Leading and trailing slashes are added when missing.
	 * @param requestParams optional name/value pairs of request parameters that will compose the query string.
	 * Use null for no parameters, use a null value for no value (just the name will be added)
	 * @param urlEncode use Boolean.TRUE to encode the parameters, null or anything else not to encode
	 * @return
	 */
	public String makeUrl(String[] segments, Map requestParams, Boolean urlEncode) {
		StringBuilder result = new StringBuilder();
		for (int i = 0; i < segments.length; i++) {
			if (segments[i]==null) {
				continue;
			}
			result.append(segments[i]);
			// Add a separator when needed
			if (i modelMap = redirectAttributes.getFlashAttributes();
		// Mette nel flash tre array di stringhe che contengono titolo, messaggio e severity.
		if (!modelMap.containsKey(KEY_NOTIFICATION_TITLE)) {
			List titles = new ArrayList<>();
			List bodies = new ArrayList<>();
			List severities = new ArrayList<>();
			redirectAttributes.addFlashAttribute(KEY_NOTIFICATION_TITLE, titles);
			redirectAttributes.addFlashAttribute(KEY_NOTIFICATION_BODY, bodies);
			redirectAttributes.addFlashAttribute(KEY_NOTIFICATION_SEVERITY, severities);
		}
		// Aggiunge i nuovi valori
		((List)modelMap.get(KEY_NOTIFICATION_TITLE)).add(title);
		((List)modelMap.get(KEY_NOTIFICATION_BODY)).add(message);
		((List)modelMap.get(KEY_NOTIFICATION_SEVERITY)).add(severity);
		String newTotalSeverity = calcTotalSeverity(modelMap, severity);
		redirectAttributes.addFlashAttribute(KEY_NOTIFICATION_TOTALSEVERITY, newTotalSeverity);
	}

	/**
	 * Test if a modal is going to be opened when back to the view (usually after a redirect)
	 * @param request
	 * @return true if a modal is going to be opened
	 * @deprecated Use YadaNotify instead
	 */
	@Deprecated
	public boolean isNotifyModalPending(HttpServletRequest request) {
		Map flashMap = RequestContextUtils.getInputFlashMap(request);
		return flashMap!=null && (
			flashMap.containsKey(KEY_NOTIFICATION_TITLE)
			|| flashMap.containsKey(KEY_NOTIFICATION_BODY)
			|| flashMap.containsKey(KEY_NOTIFICATION_SEVERITY)
			);
	}

	/**
	 * Da usare direttamente solo quando si vuole fare un redirect dopo aver mostrato un messaggio.
	 * Se chiamato tante volte, i messaggi si sommano e vengono mostrati tutti all'utente.
	 * @param title
	 * @param message
	 * @param severity a string like YadaConstants.VAL_NOTIFICATION_SEVERITY_OK
	 * @param redirectSemiurl e.g. "/user/profile"
	 * @param model
	 * @see YadaConstants
	 * @deprecated Use YadaNotify instead
	 */
	@Deprecated
	public void notifyModal(String title, String message, String severity, String redirectSemiurl, Model model) {
		if (severity==VAL_NOTIFICATION_SEVERITY_ERROR) {
			// Tutte le notifiche di errore vengono loggate a warn (potrebbero non essere degli errori del programma)
			log.warn("notifyModal: {} - {}", title, message);
		}
		// Mette nel model tre array di stringhe che contengono titolo, messaggio e severity.
		if (!model.containsAttribute(KEY_NOTIFICATION_TITLE)) {
			List titles = new ArrayList<>();
			List bodies = new ArrayList<>();
			List severities = new ArrayList<>();
			model.addAttribute(KEY_NOTIFICATION_TITLE, titles);
			model.addAttribute(KEY_NOTIFICATION_BODY, bodies);
			model.addAttribute(KEY_NOTIFICATION_SEVERITY, severities);
		}
		// Aggiunge i nuovi valori
		Map modelMap = model.asMap();
		((List)modelMap.get(KEY_NOTIFICATION_TITLE)).add(title);
		((List)modelMap.get(KEY_NOTIFICATION_BODY)).add(message);
		((List)modelMap.get(KEY_NOTIFICATION_SEVERITY)).add(severity);
		// Il redirect è sempre uno solo: prevale l'ultimo
		if (redirectSemiurl!=null) {
			model.addAttribute(KEY_NOTIFICATION_REDIRECT, redirectSemiurl);
		}
		String newTotalSeverity = calcTotalSeverity(modelMap, severity);
		model.addAttribute(KEY_NOTIFICATION_TOTALSEVERITY, newTotalSeverity);
	}

	/**
	 * Return true if modalError has been called before
	 * @param model
	 * @return
	 * @deprecated Use YadaNotify instead
	 */
	@Deprecated
	public boolean isModalError(Model model) {
		return model.asMap().containsValue(VAL_NOTIFICATION_SEVERITY_ERROR);
	}

	/**
	 * Return true if for this thread the notifyModal (or a variant) has been called
	 * @param model
	 * @return
	 * @deprecated Use YadaNotify instead
	 */
	@Deprecated
	public boolean isNotifyModalRequested(Model model) {
		return model.containsAttribute(KEY_NOTIFICATION_TITLE);
	}

	/**
	 * Calcola la severity totale del dialog dalla severity di tutti i messaggi esistenti (è la più alta tra tutti)
	 * @param modelMap
	 * @param lastSeverity
	 * @return
	 */
	@Deprecated
	private String calcTotalSeverity(Map modelMap, String lastSeverity) {
		// Algoritmo:
		// - se la total è error, resta error;
		// - se lastSeverity è ok e quella total è info, resta info;
		// - per cui
		// - se lastSeverity è error, diventa o resta error;
		// - se lastSeverity è info, diventa o resta info;
		// - se lastSeverity è ok, diventa ok visto che total non è nè error nè info;
		String newTotalSeverity = lastSeverity;
		String currentTotalSeverity = (String) modelMap.get(KEY_NOTIFICATION_TOTALSEVERITY);
		if (VAL_NOTIFICATION_SEVERITY_ERROR.equals(currentTotalSeverity)) {
			return currentTotalSeverity; // ERROR wins always
		}
		if (VAL_NOTIFICATION_SEVERITY_OK.equals(lastSeverity) && VAL_NOTIFICATION_SEVERITY_INFO.equals(currentTotalSeverity)) {
			newTotalSeverity = currentTotalSeverity; // INFO wins over OK
		}
		return newTotalSeverity;
	}

//	private void notifyModalSetValue(String title, String message, String severity, String redirectSemiurl, Model model) {
//		model.addAttribute(KEY_NOTIFICATION_TITLE, title);
//		model.addAttribute(KEY_NOTIFICATION_BODY, message);
//		model.addAttribute(KEY_NOTIFICATION_SEVERITY, severity);
//		if (redirectSemiurl!=null) {
//			model.addAttribute(KEY_NOTIFICATION_REDIRECT, redirectSemiurl);
//		}
//	}

	/**
	 * Returns the browser's remote ip address.
	 * If the connection uses a proxy that sets the "X-Forwarded-For" header, the result is taken from that header.
	 * @param request
	 * @return The client IP address, ignoring any proxy address when possible
	 */
	public String getClientAddress(HttpServletRequest request) {
		String forwardedFor = request.getHeader("X-Forwarded-For"); // X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178
		if (!StringUtils.isBlank(forwardedFor)) {
			return forwardedFor.split(",", 2)[0].trim();
		}
		return request.getRemoteAddr();
	}

	@Deprecated // Quite useless because of the format of the result
	public String getClientIp(HttpServletRequest request) {
		String remoteAddr = request.getRemoteAddr();
		String forwardedFor = request.getHeader("X-Forwarded-For");
		String remoteIp = "?";
		if (!StringUtils.isBlank(remoteAddr)) {
			remoteIp = remoteAddr;
		}
		if (!StringUtils.isBlank(forwardedFor)) {
			remoteIp = "[for " + forwardedFor + "]";
		}
		return remoteIp;
	}

	/**
	 *
	 * @param message text to be displayed (can be null for default value)
	 * @param confirmButton text of the confirm button (can be null for default value)
	 * @param cancelButton text of the cancel button (can be null for default value)
	 * @param model
	 */
	public String modalConfirm(String message, String confirmButton, String cancelButton, Model model) {
		return modalConfirm(message, confirmButton, cancelButton, model, false, false);
	}

	/**
	 * Show the confirm modal and reloads when the confirm button is pressed, adding the confirmation parameter to the url.
	 * The modal will be opened on load.
	 * Usually used by non-ajax methods.
	 * @param message text to be displayed (can be null for default value)
	 * @param confirmButton text of the confirm button (can be null for default value)
	 * @param cancelButton text of the cancel button (can be null for default value)
	 * @param model
	 */
	public String modalConfirmAndReload(String message, String confirmButton, String cancelButton, Model model) {
		return modalConfirm(message, confirmButton, cancelButton, model, true, true);
	}

	/**
	 * Show the confirm modal and optionally reloads when the confirm button is pressed
	 * @param message text to be displayed (can be null for default value)
	 * @param confirmButton text of the confirm button (can be null for default value)
	 * @param cancelButton text of the cancel button (can be null for default value)
	 * @param model
	 * @param reloadOnConfirm (optional) when true, the browser will reload on confirm, adding the confirmation parameter to the url
	 * @param openModal (optional) when true, the modal will be opened. To be used when the call is not ajax
	 */
	public String modalConfirm(String message, String confirmButton, String cancelButton, Model model, Boolean reloadOnConfirm, Boolean openModal) {
		model.addAttribute("message", message);
		model.addAttribute("confirmButton", confirmButton);
		model.addAttribute("cancelButton", cancelButton);
		if (openModal) {
			model.addAttribute("openModal", true);
		}
		if (reloadOnConfirm) {
			model.addAttribute("reloadOnConfirm", true);
		}
		return "/yada/modalConfirmB" + config.getBootstrapVersion();
	}

	/**
	 * Add a script id to call when opening the notification modal
	 * @param scriptId
	 * @param model
	 * @deprecated Use YadaNotify instead
	 */
	@Deprecated
	public void callScriptOnModal(String scriptId, Model model) {
		if (!model.containsAttribute(KEY_NOTIFICATION_CALLSCRIPT)) {
			List scriptIds = new ArrayList<>();
			model.addAttribute(KEY_NOTIFICATION_CALLSCRIPT, scriptIds);
		}
		Map modelMap = model.asMap();
		((List)modelMap.get(KEY_NOTIFICATION_CALLSCRIPT)).add(scriptId);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy