001/* 002 * Copyright 2023 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.cms.data; 017 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.stream.Collectors; 022 023import org.apache.avalon.framework.configuration.Configuration; 024import org.apache.avalon.framework.configuration.ConfigurationException; 025import org.apache.commons.collections4.ListUtils; 026import org.apache.commons.lang3.StringUtils; 027import org.apache.commons.lang3.Strings; 028import org.apache.commons.text.StringTokenizer; 029 030import org.ametys.runtime.i18n.I18nizableText; 031import org.ametys.runtime.i18n.I18nizableTextParameter; 032import org.ametys.runtime.parameter.DefaultValidator; 033import org.ametys.runtime.parameter.ValidationResult; 034 035/** 036 * {@link DefaultValidator} extension to enforce the file 'type' or extension based on the widget params. 037 */ 038public class FileValidator extends DefaultValidator 039{ 040 /** The list of file extension that are consider as image */ 041 protected static final List<String> IMAGE_FILTER = List.of("jpg", "jpeg", "gif", "png", "svg"); 042 /** The list of file extension that are considered as image */ 043 protected static final List<String> VIDEO_FILTER = List.of("ogv", "mp4", "webm", "avi", "mkv", "mov", "mpeg"); 044 /** The list of file extension that are consider as sound */ 045 protected static final List<String> SOUND_FILTER = List.of("mp3", "oga", "wav"); 046 /** The list of file extension that are consider as multimedia */ 047 protected static final List<String> MULTIMEDIA_FILTER = ListUtils.union(SOUND_FILTER, VIDEO_FILTER); 048 /** The list of file extension that are consider as pdf */ 049 protected static final List<String> PDF_FILTER = List.of("pdf"); 050 051 private List<String> _allowedExtensions; 052 053 @Override 054 public void configure(Configuration configuration) throws ConfigurationException 055 { 056 super.configure(configuration); 057 058 // Check if filtering are enabled in widget-params 059 Configuration[] params = configuration.getChild("widget-params").getChildren("param"); 060 for (Configuration param : params) 061 { 062 String name = param.getAttribute("name"); 063 if (Strings.CS.equals("allowExtensions", name)) 064 { 065 this._allowedExtensions = StringTokenizer.getCSVInstance(param.getValue()).getTokenList(); 066 } 067 else if (Strings.CS.equals("filter", name)) 068 { 069 switch (param.getValue()) 070 { 071 case "image": 072 this._allowedExtensions = IMAGE_FILTER; 073 break; 074 case "video": 075 this._allowedExtensions = VIDEO_FILTER; 076 break; 077 case "multimedia": 078 this._allowedExtensions = MULTIMEDIA_FILTER; 079 break; 080 case "audio": 081 this._allowedExtensions = SOUND_FILTER; 082 break; 083 case "pdf": 084 this._allowedExtensions = PDF_FILTER; 085 break; 086 default: 087 throw new ConfigurationException("Unsupported file filter value '" + param.getValue() + "'.", configuration); 088 } 089 } 090 } 091 } 092 093 @Override 094 protected ValidationResult validateSingleValue(Object value) 095 { 096 ValidationResult result = super.validateSingleValue(value); 097 098 if (this._allowedExtensions != null && value instanceof File file) 099 { 100 result.addResult(_checkFile(file)); 101 } 102 103 return result; 104 } 105 106 private ValidationResult _checkFile(File file) 107 { 108 ValidationResult result = new ValidationResult(); 109 110 String extension = StringUtils.substringAfterLast(file.getName(), "."); 111 extension = StringUtils.lowerCase(extension); 112 if (!this._allowedExtensions.contains(extension)) 113 { 114 Map<String, I18nizableTextParameter> params = new HashMap<>(); 115 params.put("filename", new I18nizableText(file.getName())); 116 params.put("extension", new I18nizableText(extension)); 117 params.put("allowedExtensions", new I18nizableText(this._allowedExtensions.stream().collect(Collectors.joining(", ")))); 118 result.addError(new I18nizableText("plugin.cms", "PLUGINS_CMS_FILE_VALIDATOR_INVALID_EXTENSION", params)); 119 } 120 121 return result; 122 } 123 124 @Override 125 protected ValidationResult validateArrayValues(Object[] values) 126 { 127 ValidationResult result = super.validateArrayValues(values); 128 129 if (this._allowedExtensions != null && values instanceof File[] files) 130 { 131 for (File file : files) 132 { 133 result.addResult(_checkFile(file)); 134 } 135 } 136 137 return result; 138 } 139}