001/* 002 * Copyright 2020 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.odfsync.apogee.scc; 017 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Optional; 022import java.util.Set; 023 024import org.apache.avalon.framework.activity.Initializable; 025import org.apache.avalon.framework.component.Component; 026import org.apache.avalon.framework.context.Context; 027import org.apache.avalon.framework.context.ContextException; 028import org.apache.avalon.framework.context.Contextualizable; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.cocoon.components.ContextHelper; 033import org.apache.cocoon.environment.Request; 034import org.slf4j.Logger; 035 036import org.ametys.cms.repository.Content; 037import org.ametys.cms.repository.ContentDAO; 038import org.ametys.cms.repository.ModifiableContent; 039import org.ametys.odf.ODFHelper; 040import org.ametys.odf.ProgramItem; 041import org.ametys.odf.catalog.CatalogsManager; 042import org.ametys.odf.course.Course; 043import org.ametys.odf.coursepart.CoursePart; 044import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollectionDAO; 045import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollectionHelper; 046import org.ametys.runtime.config.Config; 047 048/** 049 * Helper for Apogee Synchronizable Contents Collections. 050 */ 051public class ApogeeSynchronizableContentsCollectionHelper implements Serviceable, Component, Contextualizable, Initializable 052{ 053 /** The Avalon Role */ 054 public static final String ROLE = ApogeeSynchronizableContentsCollectionHelper.class.getName(); 055 056 /** Request attribute name to store handled contents during import or synchronization. */ 057 public static final String HANDLED_CONTENTS = AbstractApogeeSynchronizableContentsCollection.class.getName() + "$handledContents"; 058 059 /** Request attribute name to store catalog during import or synchronization. */ 060 public static final String CATALOG = AbstractApogeeSynchronizableContentsCollection.class.getName() + "$catalog"; 061 062 /** Request attribute name to store language during import or synchronization. */ 063 public static final String LANG = AbstractApogeeSynchronizableContentsCollection.class.getName() + "$lang"; 064 065 /** SCC DAO */ 066 protected SynchronizableContentsCollectionHelper _sccHelper; 067 068 /** The ODF Helper */ 069 protected ODFHelper _odfHelper; 070 071 /** SCC DAO */ 072 protected SynchronizableContentsCollectionDAO _sccDAO; 073 074 /** Content DAO */ 075 protected ContentDAO _contentDAO; 076 077 /** The catalogs manager */ 078 protected CatalogsManager _catalogsManager; 079 080 /** Context */ 081 protected Context _context; 082 083 /** Default language configured for ODF */ 084 protected String _odfLang; 085 086 087 @Override 088 public void service(ServiceManager smanager) throws ServiceException 089 { 090 _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE); 091 _sccHelper = (SynchronizableContentsCollectionHelper) smanager.lookup(SynchronizableContentsCollectionHelper.ROLE); 092 _sccDAO = (SynchronizableContentsCollectionDAO) smanager.lookup(SynchronizableContentsCollectionDAO.ROLE); 093 _contentDAO = (ContentDAO) smanager.lookup(ContentDAO.ROLE); 094 _catalogsManager = (CatalogsManager) smanager.lookup(CatalogsManager.ROLE); 095 } 096 097 public void contextualize(Context context) throws ContextException 098 { 099 _context = context; 100 } 101 102 public void initialize() throws Exception 103 { 104 _odfLang = Config.getInstance().getValue("odf.programs.lang"); 105 } 106 107 /** 108 * Synchronize the content or its children if the content has no Apogee SCC 109 * @param content the content 110 * @param logger the logger 111 */ 112 public void synchronizeContent(ModifiableContent content, Logger logger) 113 { 114 AbstractApogeeSynchronizableContentsCollection scc = getContentSCC(content, logger).orElse(null); 115 // The content has a SCC, so synchronize it 116 if (scc != null) 117 { 118 try 119 { 120 Map<String, Object> searchParams = new HashMap<>(); 121 String idField = scc.getIdField(); 122 searchParams.put(idField, content.getValue(idField)); 123 if (scc.removalSync() && scc.getTotalCount(searchParams, logger) == 0) 124 { 125 _contentDAO.forceDeleteContentsWithLog(List.of(content), null, logger); 126 } 127 else 128 { 129 scc.synchronizeContent(content, logger); 130 } 131 } 132 catch (Exception e) 133 { 134 logger.error("An error occurred synchronized content '{}' ({}) from SCC '{}'", content.getTitle(), content.getId(), scc.getId(), e); 135 } 136 } 137 // The content is manually created, so search in deeper children 138 else if (addToHandleContents(content.getId()) && content instanceof ProgramItem programItem) 139 { 140 for (ProgramItem syncChild : _odfHelper.getChildProgramItems(programItem)) 141 { 142 synchronizeContent((ModifiableContent) syncChild, logger); 143 } 144 145 if (content instanceof Course course) 146 { 147 for (CoursePart syncChild : course.getCourseParts()) 148 { 149 synchronizeContent(syncChild, logger); 150 } 151 } 152 } 153 } 154 155 /** 156 * Get the Apogee SCC for the content if it exists 157 * @param content The content to search on 158 * @param logger the logger 159 * @return the Apogee SCC 160 */ 161 public Optional<AbstractApogeeSynchronizableContentsCollection> getContentSCC(Content content, Logger logger) 162 { 163 try 164 { 165 return _sccHelper.getSynchronizableCollectionIds(content) 166 .stream() 167 .map(_sccDAO::getSynchronizableContentsCollection) 168 .filter(AbstractApogeeSynchronizableContentsCollection.class::isInstance) 169 .map(AbstractApogeeSynchronizableContentsCollection.class::cast) 170 .findFirst(); 171 } 172 catch (Exception e) 173 { 174 logger.error("An error occurred getting ametys-internal:scc property", e); 175 } 176 177 return Optional.empty(); 178 } 179 180 /** 181 * Add the content ID to the handle contents list. 182 * @param contentId Content ID 183 * @return <code>true</code> if the content ID have been added, <code>false</code> is returned if the content ID already exists in the handle contents list. 184 */ 185 public boolean addToHandleContents(String contentId) 186 { 187 return _addContentToRequestAttribute(contentId, HANDLED_CONTENTS); 188 } 189 190 private boolean _addContentToRequestAttribute(String contentId, String attributeName) 191 { 192 Request request = ContextHelper.getRequest(_context); 193 @SuppressWarnings("unchecked") 194 Set<String> handleContents = (Set<String>) request.getAttribute(attributeName); 195 boolean added = handleContents.add(contentId); 196 request.setAttribute(attributeName, handleContents); 197 return added; 198 } 199 200 /** 201 * Get the synchronization catalog, can be the one defined by the synchronized content 202 * or the default catalog. 203 * @return the synchronization catalog, can be null 204 */ 205 public String getSynchronizationCatalog() 206 { 207 return Optional.of(ContextHelper.getRequest(_context)) 208 .map(r -> (String) r.getAttribute(CATALOG)) 209 .orElseGet(() -> _catalogsManager.getDefaultCatalogName()); 210 } 211 212 /** 213 * Get the synchronization language, can be the one defined by the synchronized content 214 * or the ODF language. 215 * @return the synchronization language 216 */ 217 public String getSynchronizationLang() 218 { 219 return Optional.of(ContextHelper.getRequest(_context)) 220 .map(r -> (String) r.getAttribute(LANG)) 221 .orElse(_odfLang); 222 } 223}