001/* 002 * Copyright 2025 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.odf.observation.skill; 017 018import java.util.Arrays; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Optional; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026 027import org.ametys.cms.ObservationConstants; 028import org.ametys.cms.data.ContentValue; 029import org.ametys.cms.repository.Content; 030import org.ametys.cms.trash.element.TrashElementDAO; 031import org.ametys.core.observation.Event; 032import org.ametys.core.observation.ObservationManager; 033import org.ametys.core.user.CurrentUserProvider; 034import org.ametys.plugins.repository.trash.TrashElement; 035 036/** 037 * Observer to delete skills that became orphans after the deletion of their parent. 038 * When the deleted content is a Program, delete the linked macro skills that would become orphans 039 * The deletion of macro skills will trigger the deletion of its micro skills 040 * When the deleted content is a Macro skill, delete the linked micro skills that would become orphans 041 */ 042public class DeleteContentSkillStep2Observer extends AbstractSkillsStepObserver 043{ 044 private CurrentUserProvider _currentUserProvider; 045 private ObservationManager _observationManager; 046 private TrashElementDAO _trashElementDAO; 047 048 @Override 049 public void service(ServiceManager manager) throws ServiceException 050 { 051 super.service(manager); 052 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 053 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 054 _trashElementDAO = (TrashElementDAO) manager.lookup(org.ametys.plugins.repository.trash.TrashElementDAO.ROLE); 055 } 056 057 @Override 058 protected String getSupportedEventId() 059 { 060 return ObservationConstants.EVENT_CONTENT_DELETED; 061 } 062 063 public void observe(Event event, Map<String, Object> transientVars) throws Exception 064 { 065 String contentId = (String) event.getArguments().get(ObservationConstants.ARGS_CONTENT_ID); 066 // When the content is modified, we need to check if the skills are still linked to the content 067 ContentValue[] previousSkills = _getRequestAttribute("previousSkills", contentId); 068 // Remove the orphan skills 069 if (previousSkills != null && previousSkills.length > 0) 070 { 071 // Check if the original parent was trashed or deleted 072 TrashElement trashElement = _trashElementDAO.find(contentId); 073 074 if (trashElement != null) 075 { 076 // The parent is deleted so all previous values need to be deleted 077 List<String> skillsToDelete = Arrays.stream(previousSkills) 078 .map(ContentValue::getContentId) 079 .toList(); 080 081 // Trash skills 082 _contentDAO.trashContents(skillsToDelete, true); 083 084 for (String deletedChild : skillsToDelete) 085 { 086 TrashElement trashChild = _trashElementDAO.find(deletedChild); 087 if (trashChild != null) 088 { 089 trashChild.setHidden(true); 090 trashChild.saveChanges(); 091 092 // Notify observers 093 Map<String, Object> eventParams = new HashMap<>(); 094 eventParams.put(org.ametys.cms.ObservationConstants.ARGS_TRASH_ELEMENT_ID, trashChild.getId()); 095 eventParams.put(org.ametys.cms.ObservationConstants.ARGS_AMETYS_OBJECT_ID, contentId); 096 _observationManager.notify(new Event(org.ametys.cms.ObservationConstants.EVENT_TRASH_UPDATED, _currentUserProvider.getUser(), eventParams)); 097 } 098 } 099 100 trashElement.addLinkedObjects(skillsToDelete.toArray(String[]::new)); 101 trashElement.saveChanges(); 102 103 // Notify observers 104 Map<String, Object> eventParams = new HashMap<>(); 105 eventParams.put(org.ametys.cms.ObservationConstants.ARGS_TRASH_ELEMENT_ID, trashElement.getId()); 106 eventParams.put(org.ametys.cms.ObservationConstants.ARGS_AMETYS_OBJECT_ID, trashElement.getAmetysObjectId()); 107 _observationManager.notify(new Event(org.ametys.cms.ObservationConstants.EVENT_TRASH_UPDATED, _currentUserProvider.getUser(), eventParams)); 108 } 109 else 110 { 111 List<Content> skillsToDelete = Arrays.stream(previousSkills) 112 .map(ContentValue::getContentIfExists) 113 .filter(Optional::isPresent) 114 .map(Optional::get) 115 .map(Content.class::cast) 116 .toList(); 117 118 if (!skillsToDelete.isEmpty()) 119 { 120 // Remove the orphan skills 121 _contentDAO.forceDeleteContentsObj(skillsToDelete, null); 122 } 123 } 124 } 125 } 126}