001/* 002 * Copyright 2015 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.sms.action; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.IOException; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.Map; 026import java.util.Set; 027 028import org.apache.avalon.framework.parameters.Parameters; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.cocoon.acting.ServiceableAction; 032import org.apache.cocoon.environment.ObjectModelHelper; 033import org.apache.cocoon.environment.Redirector; 034import org.apache.cocoon.environment.Request; 035import org.apache.cocoon.environment.SourceResolver; 036import org.apache.cocoon.servlet.multipart.Part; 037import org.apache.cocoon.servlet.multipart.PartOnDisk; 038import org.apache.cocoon.servlet.multipart.RejectedPart; 039import org.apache.commons.io.FilenameUtils; 040import org.apache.commons.io.IOUtils; 041import org.apache.commons.io.input.BOMInputStream; 042 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.plugins.sms.SMSHelper; 045import org.ametys.plugins.sms.dao.SubscriberDAO; 046 047/** 048 * Action importing subscribers from a CSV file supposedly containing one valid number per line 049 */ 050public class ImportSMSListSubscribersAction extends ServiceableAction 051{ 052 private static final String[] __ALLOWED_EXTENSIONS = new String[] {"txt", "csv"}; 053 054 /** The subscribers DAO. */ 055 private SubscriberDAO _subscriberDAO; 056 057 /** The Ametys object resolver */ 058 private AmetysObjectResolver _ametysObjectResolver; 059 060 @Override 061 public void service(ServiceManager smanager) throws ServiceException 062 { 063 super.service(smanager); 064 _subscriberDAO = (SubscriberDAO) smanager.lookup(SubscriberDAO.ROLE); 065 _ametysObjectResolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 066 } 067 068 @Override 069 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 070 { 071 Map<String, String> result = new HashMap<>(); 072 073 Request request = ObjectModelHelper.getRequest(objectModel); 074 075 String smsListId = request.getParameter("smsListId"); 076 boolean cleanTable = "true".equals(request.getParameter("cleanSubscribers")); 077 078 Part part = (Part) request.get("importFile"); 079 if (part instanceof RejectedPart) 080 { 081 return Collections.singletonMap("error", "rejected-file"); 082 } 083 084 PartOnDisk uploadedFilePart = (PartOnDisk) part; 085 File uploadedFile = (uploadedFilePart != null) ? uploadedFilePart.getFile() : null; 086 String filename = (uploadedFilePart != null) ? uploadedFilePart.getFileName().toLowerCase() : null; 087 088 if (!FilenameUtils.isExtension(filename, __ALLOWED_EXTENSIONS)) 089 { 090 return Collections.singletonMap("error", "invalid-extension"); 091 } 092 093 // Test if the sms list exists. 094 if (!_ametysObjectResolver.hasAmetysObjectForId(smsListId)) 095 { 096 return Collections.singletonMap("error", "unknown-list"); 097 } 098 099 Collection<String> phoneNumbers = null; 100 try (FileInputStream fileIS = new FileInputStream(uploadedFile); 101 BOMInputStream bomIS = new BOMInputStream(fileIS);) 102 { 103 // Extract the phone numbers from the file. 104 phoneNumbers = _getPhoneNumbers(bomIS, result); 105 } 106 107 if (!phoneNumbers.isEmpty()) 108 { 109 // Empty the broadcasting list subscribers if needed. 110 if (cleanTable) 111 { 112 _subscriberDAO.deleteAllNumbers(smsListId); 113 } 114 115 // Insert the phone numbers. 116 _insertSubscribers(phoneNumbers, smsListId, result); 117 } 118 else 119 { 120 result.put("subscribedCount", "0"); 121 result.put("existingCount", "0"); 122 result.put("errorCount", "0"); 123 } 124 125 result.put("success", "true"); 126 result.put("smsListId", smsListId); 127 128 return result; 129 } 130 131 /** 132 * Extract the phone numbers from the file. 133 * @param bomIS the BOM input stream 134 * @param result the result map 135 * @return the collection of phone numbers. 136 * @throws IOException if an error occurs reading the file. 137 */ 138 private Set<String> _getPhoneNumbers(BOMInputStream bomIS, Map<String, String> result) throws IOException 139 { 140 Set<String> phoneNumbers = new HashSet<>(); 141 int errorCount = 0; 142 143 for (String phoneNumber : IOUtils.readLines(bomIS)) 144 { 145 // We check if the phone number is correct 146 String transformedNumber = phoneNumber.trim(); 147 transformedNumber = SMSHelper.transformPhoneNumber(transformedNumber); 148 149 if (!SMSHelper.checkPhoneNumber(transformedNumber) && !SMSHelper.PHONE_NUMBER_INTERNATIONAL_VALIDATOR.matcher(transformedNumber).matches()) 150 { 151 getLogger().warn("Import subscribers phone number: '" + phoneNumber + "' is not a valid phone number; it will be ignored"); 152 errorCount++; 153 } 154 else 155 { 156 phoneNumbers.add(transformedNumber); 157 } 158 } 159 result.put("errorCount", Integer.toString(errorCount)); 160 return phoneNumbers; 161 } 162 163 /** 164 * Insert subscribers in the broadcasting list 165 * @param phoneNumbers the list of phone numbers to insert 166 * @param smsListId the id of the sms list 167 * @param result the server call result 168 */ 169 private void _insertSubscribers(Collection<String> phoneNumbers, String smsListId, Map<String, String> result) 170 { 171 int subscribedCount = 0; 172 int existingCount = 0; 173 int errorCount = Integer.parseInt(result.get("errorCount")); 174 175 for (String phoneNumber : phoneNumbers) 176 { 177 try 178 { 179 if (_subscriberDAO.numberAlreadyExists(phoneNumber, smsListId)) 180 { 181 existingCount++; 182 } 183 else 184 { 185 _subscriberDAO.insertNumber(phoneNumber, smsListId); 186 187 if (getLogger().isInfoEnabled()) 188 { 189 getLogger().info("The user with phoneNumber '" + phoneNumber + "' subscribed to the list '" + smsListId + "'."); 190 } 191 192 subscribedCount++; 193 } 194 } 195 catch (Exception e) 196 { 197 getLogger().error("Impossible to add as a subscriber the phoneNumber " + phoneNumber + " in list '" + smsListId + "'.", e); 198 errorCount++; 199 } 200 } 201 202 result.put("subscribedCount", Integer.toString(subscribedCount)); 203 result.put("existingCount", Integer.toString(existingCount)); 204 result.put("errorCount", Integer.toString(errorCount)); 205 } 206}