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