001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.sitemap; 029 030import static org.opencms.ade.sitemap.shared.I_CmsAliasConstants.PARAM_IMPORTFILE; 031import static org.opencms.ade.sitemap.shared.I_CmsAliasConstants.PARAM_SEPARATOR; 032import static org.opencms.ade.sitemap.shared.I_CmsAliasConstants.PARAM_SITEROOT; 033 034import org.opencms.db.CmsAlias; 035import org.opencms.db.CmsAliasManager; 036import org.opencms.db.CmsRewriteAlias; 037import org.opencms.file.CmsObject; 038import org.opencms.file.CmsResource; 039import org.opencms.file.CmsResourceFilter; 040import org.opencms.gwt.shared.alias.CmsAliasEditValidationReply; 041import org.opencms.gwt.shared.alias.CmsAliasEditValidationRequest; 042import org.opencms.gwt.shared.alias.CmsAliasImportResult; 043import org.opencms.gwt.shared.alias.CmsAliasSaveValidationRequest; 044import org.opencms.gwt.shared.alias.CmsAliasTableRow; 045import org.opencms.gwt.shared.alias.CmsRewriteAliasTableRow; 046import org.opencms.main.CmsException; 047import org.opencms.main.OpenCms; 048import org.opencms.util.CmsUUID; 049 050import java.util.ArrayList; 051import java.util.HashSet; 052import java.util.List; 053import java.util.Locale; 054import java.util.Set; 055 056import javax.servlet.http.HttpServletRequest; 057import javax.servlet.http.HttpServletResponse; 058 059import org.apache.commons.fileupload.FileItem; 060import org.apache.commons.fileupload.FileItemFactory; 061import org.apache.commons.fileupload.disk.DiskFileItemFactory; 062import org.apache.commons.fileupload.servlet.ServletFileUpload; 063 064import com.google.common.collect.Sets; 065 066/** 067 * Helper class used by a service to edit or import aliases for a whole site.<p> 068 */ 069public class CmsAliasBulkEditHelper { 070 071 /** The current CMS context. */ 072 private CmsObject m_cms; 073 074 /** Flag to indicate whether the validation was successful. */ 075 private boolean m_hasErrors; 076 077 /** 078 * Creates a new helper object.<p> 079 * 080 * @param cms the current CMS context 081 */ 082 public CmsAliasBulkEditHelper(CmsObject cms) { 083 084 m_cms = cms; 085 } 086 087 /** 088 * Imports uploaded aliases from a request.<p> 089 * 090 * @param request the request containing the uploaded aliases 091 * @param response the response 092 * @throws Exception if something goes wrong 093 */ 094 public void importAliases(HttpServletRequest request, HttpServletResponse response) throws Exception { 095 096 FileItemFactory factory = new DiskFileItemFactory(); 097 ServletFileUpload upload = new ServletFileUpload(factory); 098 @SuppressWarnings("unchecked") 099 List<FileItem> items = upload.parseRequest(request); 100 byte[] data = null; 101 String siteRoot = null; 102 String separator = ","; 103 for (FileItem fileItem : items) { 104 String name = fileItem.getFieldName(); 105 if (PARAM_IMPORTFILE.equals(name)) { 106 data = fileItem.get(); 107 } else if (PARAM_SITEROOT.equals(name)) { 108 siteRoot = new String(fileItem.get(), request.getCharacterEncoding()); 109 } else if (PARAM_SEPARATOR.equals(name)) { 110 separator = new String(fileItem.get(), request.getCharacterEncoding()); 111 } 112 } 113 List<CmsAliasImportResult> result = new ArrayList<CmsAliasImportResult>(); 114 if ((siteRoot != null) && (data != null)) { 115 result = OpenCms.getAliasManager().importAliases(m_cms, data, siteRoot, separator); 116 } 117 String key = CmsVfsSitemapService.addAliasImportResult(result); 118 // only respond with a key, then the client can get the data for the key via GWT-RPC 119 response.getWriter().print(key); 120 } 121 122 /** 123 * Saves alias changes to the database.<p> 124 * 125 * @param saveRequest an object containing the alias changes to save 126 * 127 * @return a validation error if the alias data is invalid, or null otherwise 128 * 129 * @throws CmsException if something goes wrong 130 */ 131 public CmsAliasEditValidationReply saveAliases(CmsAliasSaveValidationRequest saveRequest) throws CmsException { 132 133 CmsAliasEditValidationReply reply = validateAliases(saveRequest); 134 if (m_hasErrors) { 135 return reply; 136 } else { 137 List<CmsRewriteAliasTableRow> rewriteData = saveRequest.getRewriteData(); 138 OpenCms.getAliasManager().saveRewriteAliases( 139 m_cms, 140 m_cms.getRequestContext().getSiteRoot(), 141 convertRewriteData(rewriteData)); 142 Set<CmsUUID> allTouchedIds = new HashSet<CmsUUID>(); 143 List<CmsAliasTableRow> rows = saveRequest.getEditedData(); 144 for (CmsAliasTableRow row : rows) { 145 if (row.isEdited()) { 146 allTouchedIds.add(row.getStructureId()); 147 if (row.getOriginalStructureId() != null) { 148 allTouchedIds.add(row.getOriginalStructureId()); 149 } 150 } 151 } 152 allTouchedIds.addAll(saveRequest.getDeletedIds()); 153 CmsAliasManager aliasManager = OpenCms.getAliasManager(); 154 String siteRoot = saveRequest.getSiteRoot(); 155 List<CmsAlias> aliases = aliasManager.getAliasesForSite(m_cms, siteRoot); 156 Set<CmsAlias> aliasSet = new HashSet<CmsAlias>(); 157 Set<CmsAlias> editedAliasSet = new HashSet<CmsAlias>(); 158 aliasSet.addAll(aliases); 159 for (CmsAliasTableRow row : rows) { 160 CmsAlias editedAlias = new CmsAlias(row.getStructureId(), siteRoot, row.getAliasPath(), row.getMode()); 161 editedAliasSet.add(editedAlias); 162 } 163 Set<CmsAlias> toDelete = Sets.difference(aliasSet, editedAliasSet); 164 toDelete = filterStructureId(toDelete, allTouchedIds); 165 Set<CmsAlias> toAdd = Sets.difference(editedAliasSet, aliasSet); 166 toAdd = filterStructureId(toAdd, allTouchedIds); 167 168 aliasManager.updateAliases(m_cms, toDelete, toAdd); 169 return null; 170 } 171 } 172 173 /** 174 * Validates the alias data.<p> 175 * 176 * @param validationRequest an object containing the alias data to validate 177 * 178 * @return the validation result 179 */ 180 public CmsAliasEditValidationReply validateAliases(CmsAliasEditValidationRequest validationRequest) { 181 182 List<CmsAliasTableRow> editedData = validationRequest.getEditedData(); 183 CmsAliasTableRow newEntry = validationRequest.getNewEntry(); 184 if (newEntry != null) { 185 newEntry.setKey((new CmsUUID()).toString()); 186 editedData.add(newEntry); 187 188 } 189 CmsObject cms = m_cms; 190 Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 191 for (CmsAliasTableRow row : editedData) { 192 row.clearErrors(); 193 validateSingleAliasRow(cms, row); 194 } 195 Set<String> foundAliasPaths = new HashSet<String>(); 196 Set<String> duplicateAliasPaths = new HashSet<String>(); 197 for (CmsAliasTableRow row : editedData) { 198 String aliasPath = row.getAliasPath(); 199 if (foundAliasPaths.contains(aliasPath)) { 200 duplicateAliasPaths.add(aliasPath); 201 } 202 foundAliasPaths.add(aliasPath); 203 } 204 for (CmsAliasTableRow row : editedData) { 205 if (duplicateAliasPaths.contains(row.getAliasPath())) { 206 if (row.getPathError() == null) { 207 row.setAliasError(messageDuplicateAliasPath(locale)); 208 m_hasErrors = true; 209 } 210 } 211 } 212 CmsAliasEditValidationReply result = new CmsAliasEditValidationReply(); 213 List<CmsAliasTableRow> changedRows = new ArrayList<CmsAliasTableRow>(); 214 for (CmsAliasTableRow row : editedData) { 215 if (row.isChanged()) { 216 changedRows.add(row); 217 } 218 } 219 if (newEntry != null) { 220 changedRows.remove(newEntry); 221 } 222 result.setChangedRows(changedRows); 223 result.setValidatedNewEntry(newEntry); 224 return result; 225 } 226 227 /** 228 * Filters all aliases from a set whose structure id is in a given set of structure ids.<p> 229 * 230 * @param aliases the aliases to filter 231 * @param structureIds the structure ids for which we want the aliases 232 * 233 * @return the filtered structure ids 234 */ 235 protected Set<CmsAlias> filterStructureId(Set<CmsAlias> aliases, Set<CmsUUID> structureIds) { 236 237 Set<CmsAlias> result = new HashSet<CmsAlias>(); 238 for (CmsAlias alias : aliases) { 239 if (structureIds.contains(alias.getStructureId())) { 240 result.add(alias); 241 } 242 } 243 return result; 244 } 245 246 /** 247 * Converts rewrite alias table rows to rewrite alias objects.<p> 248 * 249 * @param rewriteData the rewrite data 250 * 251 * @return the converted rewrite aliases 252 */ 253 private List<CmsRewriteAlias> convertRewriteData(List<CmsRewriteAliasTableRow> rewriteData) { 254 255 String siteRoot = m_cms.getRequestContext().getSiteRoot(); 256 List<CmsRewriteAlias> result = new ArrayList<CmsRewriteAlias>(); 257 for (CmsRewriteAliasTableRow row : rewriteData) { 258 CmsRewriteAlias alias = new CmsRewriteAlias( 259 row.getId(), 260 siteRoot, 261 row.getPatternString(), 262 row.getReplacementString(), 263 row.getMode()); 264 result.add(alias); 265 } 266 return result; 267 } 268 269 /** 270 * Message accessor. 271 * 272 * @param locale the locale for messages 273 * 274 * @return the message string 275 */ 276 private String messageDuplicateAliasPath(Locale locale) { 277 278 return Messages.get().getBundle(locale).key(Messages.ERR_ALIAS_DUPLICATE_ALIAS_PATH_0); 279 } 280 281 /** 282 * Message accessor. 283 * 284 * @param locale the locale for messages 285 * 286 * @return the message string 287 */ 288 private String messageInvalidAliasPath(Locale locale) { 289 290 return Messages.get().getBundle(locale).key(Messages.ERR_ALIAS_INVALID_ALIAS_PATH_0); 291 } 292 293 /** 294 * Message accessor. 295 * 296 * @param locale the locale for messages 297 * 298 * @return the message string 299 */ 300 private String messageResourceNotFound(Locale locale) { 301 302 return Messages.get().getBundle(locale).key(Messages.ERR_ALIAS_RESOURCE_NOT_FOUND_0); 303 304 } 305 306 /** 307 * Validates a single alias row.<p> 308 * 309 * @param cms the current CMS context 310 * @param row the row to validate 311 */ 312 private void validateSingleAliasRow(CmsObject cms, CmsAliasTableRow row) { 313 314 Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 315 if (row.getStructureId() == null) { 316 String path = row.getResourcePath(); 317 try { 318 CmsResource resource = cms.readResource(path, CmsResourceFilter.ALL); 319 row.setStructureId(resource.getStructureId()); 320 if (row.getOriginalStructureId() == null) { 321 row.setOriginalStructureId(resource.getStructureId()); 322 } 323 } catch (CmsException e) { 324 row.setPathError(messageResourceNotFound(locale)); 325 m_hasErrors = true; 326 } 327 } 328 if (!CmsAlias.ALIAS_PATTERN.matcher(row.getAliasPath()).matches()) { 329 row.setAliasError(messageInvalidAliasPath(locale)); 330 m_hasErrors = true; 331 } 332 } 333}