001/* 002 * Copyright 2016 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.odfweb.userpref; 017 018import java.sql.Connection; 019import java.sql.PreparedStatement; 020import java.sql.ResultSet; 021import java.sql.SQLException; 022import java.util.ArrayList; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.avalon.framework.component.Component; 029import org.apache.avalon.framework.configuration.Configurable; 030import org.apache.avalon.framework.configuration.Configuration; 031import org.apache.avalon.framework.configuration.ConfigurationException; 032import org.apache.avalon.framework.logger.AbstractLogEnabled; 033import org.apache.commons.lang.StringUtils; 034 035import org.ametys.core.datasource.ConnectionHelper; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.core.userpref.UserPreferencesException; 038import org.ametys.core.userpref.UserPreferencesStorage; 039import org.ametys.odf.course.Course; 040import org.ametys.odf.program.Program; 041import org.ametys.runtime.config.Config; 042 043 044/** 045 * Specific storage for odf cart user preferences 046 */ 047public class ODFCartUserPreferencesStorage extends AbstractLogEnabled implements UserPreferencesStorage, Configurable, Component 048{ 049 /** The Avalon Role */ 050 public static final String ROLE = ODFCartUserPreferencesStorage.class.getName(); 051 052 /** The id of user preference for ids for programs */ 053 public static final String USER_PREF_PROGRAM_IDS = "cartProgramIds"; 054 /** The id of user preference for ids for courses */ 055 public static final String USER_PREF_COURSE_IDS = "cartCourseIds"; 056 057 /** The id of the data source used. */ 058 protected String _dataSourceId; 059 060 /** The login column, cannot be null. */ 061 protected String _loginColumn; 062 063 /** The population id column, cannot be null. */ 064 protected String _populationColumn; 065 066 /** The context column, can be null if the database is not context-dependent. */ 067 protected String _contextColumn; 068 069 /** The content id column */ 070 protected String _contentIdColumn; 071 072 /** Mapping from preference id to table name. */ 073 protected Map<String, String> _prefIdToTable; 074 075 @Override 076 public void configure(Configuration configuration) throws ConfigurationException 077 { 078 // Data source id 079 Configuration dataSourceConf = configuration.getChild("datasource", false); 080 if (dataSourceConf == null) 081 { 082 throw new ConfigurationException("The 'datasource' configuration node must be defined.", dataSourceConf); 083 } 084 085 String dataSourceConfParam = dataSourceConf.getValue(); 086 String dataSourceConfType = dataSourceConf.getAttribute("type", "config"); 087 088 if (StringUtils.equals(dataSourceConfType, "config")) 089 { 090 _dataSourceId = Config.getInstance().getValueAsString(dataSourceConfParam); 091 } 092 else // expecting type="id" 093 { 094 _dataSourceId = dataSourceConfParam; 095 } 096 097 // Default to "contentId". 098 _contentIdColumn = configuration.getChild("content-id").getValue("contentId"); 099 // Default to "login". 100 _loginColumn = configuration.getChild("loginColumn").getValue("login").toLowerCase(); 101 // Default to "population" 102 _populationColumn = configuration.getChild("populationColumn").getValue("population").toLowerCase(); 103 // Default to 'context' (no context column). 104 _contextColumn = configuration.getChild("contextColumn").getValue("context"); 105 106 // Configure the preference-table mappings. 107 configureMappings(configuration.getChild("mappings")); 108 } 109 110 /** 111 * Configure the mappings from preference ID to table name. 112 * @param configuration the mapping configuration root. 113 * @throws ConfigurationException if an error occurs. 114 */ 115 public void configureMappings(Configuration configuration) throws ConfigurationException 116 { 117 _prefIdToTable = new HashMap<>(); 118 119 for (Configuration mappingConf : configuration.getChildren("mapping")) 120 { 121 String prefId = mappingConf.getAttribute("prefId"); 122 String table = mappingConf.getAttribute("table"); 123 124 _prefIdToTable.put(prefId, table); 125 } 126 } 127 128 @Override 129 public Map<String, String> getUnTypedUserPrefs(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 130 { 131 Map<String, String> prefs = new HashMap<>(); 132 133 for (String id : _prefIdToTable.keySet()) 134 { 135 prefs.put(id, getUserPreferenceAsString(user, storageContext, contextVars, id)); 136 } 137 138 return prefs; 139 } 140 141 @Override 142 public void removeUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars) throws UserPreferencesException 143 { 144 Connection connection = null; 145 try 146 { 147 connection = ConnectionHelper.getConnection(_dataSourceId); 148 for (String id : _prefIdToTable.keySet()) 149 { 150 _removeUserPreferencesFromTable(connection, user, storageContext, _prefIdToTable.get(id)); 151 } 152 } 153 catch (SQLException e) 154 { 155 String message = "Database error trying to remove ODF cart preferences for user '" + user + "' in context '" + storageContext + "'."; 156 getLogger().error(message, e); 157 throw new UserPreferencesException(message, e); 158 } 159 finally 160 { 161 ConnectionHelper.cleanup(connection); 162 } 163 } 164 165 private void _removeUserPreferencesFromTable(Connection connection, UserIdentity user, String storageContext, String table) throws SQLException 166 { 167 PreparedStatement stmt = null; 168 169 try 170 { 171 StringBuilder query = new StringBuilder(); 172 query.append("DELETE FROM ").append(table).append(" WHERE ").append(_loginColumn).append(" = ? AND ").append(_populationColumn).append(" = ?"); 173 query.append(" AND ").append(_contextColumn).append(" = ?"); 174 175 stmt = connection.prepareStatement(query.toString()); 176 stmt.setString(1, user.getLogin()); 177 stmt.setString(2, user.getPopulationId()); 178 stmt.setString(3, storageContext); 179 180 stmt.executeUpdate(); 181 } 182 finally 183 { 184 ConnectionHelper.cleanup(stmt); 185 } 186 187 } 188 189 /** 190 * Remove all user preferences referencing a {@link Program} 191 * @param programId The id of program to remove from user preferences 192 * @throws UserPreferencesException if failed to delete users preferences 193 */ 194 public void removeProgramFromUserPreferences(String programId) throws UserPreferencesException 195 { 196 _removeContentFromUserPreferences(programId, _prefIdToTable.get(USER_PREF_PROGRAM_IDS)); 197 } 198 199 /** 200 * Remove all user preferences referencing a {@link Course} 201 * @param courseId The id of course to remove from user preferences 202 * @throws UserPreferencesException if failed to delete users preferences 203 */ 204 public void removeCourseFromUserPreferences(String courseId) throws UserPreferencesException 205 { 206 _removeContentFromUserPreferences(courseId, _prefIdToTable.get(USER_PREF_COURSE_IDS)); 207 } 208 209 private void _removeContentFromUserPreferences(String contentId, String tableName) throws UserPreferencesException 210 { 211 Connection connection = null; 212 PreparedStatement stmt = null; 213 214 try 215 { 216 connection = ConnectionHelper.getConnection(_dataSourceId); 217 218 StringBuilder query = new StringBuilder(); 219 query.append("DELETE FROM ").append(tableName).append(" WHERE ").append(_contentIdColumn).append(" = ?"); 220 221 stmt = connection.prepareStatement(query.toString()); 222 223 stmt.setString(1, contentId); 224 stmt.executeUpdate(); 225 } 226 catch (SQLException e) 227 { 228 String message = "Database error trying to remove ODF content of id '" + contentId + "' from all user's preferences"; 229 getLogger().error(message, e); 230 throw new UserPreferencesException(message, e); 231 } 232 finally 233 { 234 ConnectionHelper.cleanup(stmt); 235 ConnectionHelper.cleanup(connection); 236 } 237 } 238 239 @Override 240 public void setUserPreferences(UserIdentity user, String storageContext, Map<String, String> contextVars, Map<String, String> preferences) throws UserPreferencesException 241 { 242 removeUserPreferences(user, storageContext, contextVars); 243 244 Connection connection = null; 245 try 246 { 247 connection = ConnectionHelper.getConnection(_dataSourceId); 248 _insertPreferences(connection, preferences, user, storageContext); 249 } 250 catch (SQLException e) 251 { 252 String message = "Database error trying to set ODF cart preferences of user '" + user + "' in context '" + storageContext + "'."; 253 getLogger().error(message, e); 254 throw new UserPreferencesException(message, e); 255 } 256 finally 257 { 258 ConnectionHelper.cleanup(connection); 259 } 260 } 261 262 private void _insertPreferences(Connection connection, Map<String, String> preferences, UserIdentity user, String storageContext) throws SQLException 263 { 264 for (String id : preferences.keySet()) 265 { 266 String table = _prefIdToTable.get(id); 267 268 String[] contentIdsTab = StringUtils.split(preferences.get(id), ","); 269 270 if (contentIdsTab.length > 0) 271 { 272 PreparedStatement stmt = null; 273 try 274 { 275 StringBuilder query = new StringBuilder(); 276 query.append("INSERT INTO ").append(table).append("(").append(_loginColumn).append(", ").append(_populationColumn); 277 query.append(", ").append(_contextColumn); 278 query.append(", ").append(_contentIdColumn); 279 280 StringBuilder values = new StringBuilder(); 281 for (int i = 0; i < contentIdsTab.length; i++) 282 { 283 if (i != 0) 284 { 285 values.append(","); 286 } 287 values.append("(?, ?, ?, ?)"); 288 } 289 290 query.append(") VALUES ").append(values); 291 292 int i = 1; 293 stmt = connection.prepareStatement(query.toString()); 294 295 for (String idContent : contentIdsTab) 296 { 297 stmt.setString(i++, user.getLogin()); 298 stmt.setString(i++, user.getPopulationId()); 299 stmt.setString(i++, storageContext); 300 stmt.setString(i++, idContent); 301 } 302 303 stmt.executeUpdate(); 304 } 305 finally 306 { 307 ConnectionHelper.cleanup(stmt); 308 } 309 } 310 } 311 312 } 313 314 @Override 315 public String getUserPreferenceAsString(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 316 { 317 Connection connection = null; 318 PreparedStatement statement = null; 319 ResultSet rs = null; 320 String value = null; 321 String table = _prefIdToTable.get(id); 322 323 try 324 { 325 StringBuilder query = new StringBuilder(); 326 query.append("SELECT ").append(_contentIdColumn).append(" FROM ").append(table).append(" WHERE ").append(_loginColumn).append(" = ? AND ").append(_populationColumn).append(" = ?"); 327 query.append(" AND ").append(_contextColumn).append(" = ?"); 328 329 connection = ConnectionHelper.getConnection(_dataSourceId); 330 331 statement = connection.prepareStatement(query.toString()); 332 statement.setString(1, user.getLogin()); 333 statement.setString(2, user.getPopulationId()); 334 statement.setString(3, storageContext); 335 336 rs = statement.executeQuery(); 337 338 List<String> results = new ArrayList<>(); 339 while (rs.next()) 340 { 341 results.add(rs.getString(1)); 342 } 343 344 value = StringUtils.join(results, ","); 345 } 346 catch (SQLException e) 347 { 348 String message = "Database error trying to get ODF cart preferences of user '" + user + "' in context '" + storageContext + "'."; 349 getLogger().error(message, e); 350 throw new UserPreferencesException(message, e); 351 } 352 finally 353 { 354 ConnectionHelper.cleanup(rs); 355 ConnectionHelper.cleanup(statement); 356 ConnectionHelper.cleanup(connection); 357 } 358 359 return value; 360 } 361 362 @Override 363 public Long getUserPreferenceAsLong(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 364 { 365 return null; 366 } 367 368 @Override 369 public Date getUserPreferenceAsDate(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 370 { 371 return null; 372 } 373 374 @Override 375 public Boolean getUserPreferenceAsBoolean(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 376 { 377 return null; 378 } 379 380 @Override 381 public Double getUserPreferenceAsDouble(UserIdentity user, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException 382 { 383 return null; 384 } 385 386}