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.web.indexing.observation; 017 018import java.util.List; 019import java.util.Map; 020 021import org.apache.avalon.framework.context.Context; 022import org.apache.avalon.framework.context.ContextException; 023import org.apache.avalon.framework.context.Contextualizable; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027import org.apache.cocoon.components.ContextHelper; 028import org.apache.cocoon.environment.Request; 029import org.apache.commons.lang.StringUtils; 030 031import org.ametys.cms.content.indexing.solr.SolrIndexer; 032import org.ametys.cms.content.indexing.solr.observation.ObserverHelper; 033import org.ametys.cms.repository.Content; 034import org.ametys.core.observation.Event; 035import org.ametys.core.observation.Observer; 036import org.ametys.plugins.repository.AmetysObjectResolver; 037import org.ametys.plugins.repository.RepositoryConstants; 038import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 039import org.ametys.runtime.plugin.component.AbstractLogEnabled; 040import org.ametys.web.ObservationConstants; 041import org.ametys.web.WebConstants; 042import org.ametys.web.repository.content.WebContent; 043import org.ametys.web.repository.page.Page; 044import org.ametys.web.repository.page.PageDAO; 045import org.ametys.web.repository.page.ZoneItem.ZoneType; 046 047/** 048 * Observes when a content is made orphan or reaffected to a page and update the index accordingly. 049 */ 050public class ContentOrphanStatusPart2Observer extends AbstractLogEnabled implements Observer, Serviceable, Contextualizable 051{ 052 /** The Solr indexer. */ 053 protected SolrIndexer _solrIndexer; 054 /** The Ametys object resolver */ 055 protected AmetysObjectResolver _resolver; 056 /** The page DAO */ 057 protected PageDAO _pageDAO; 058 /** The avalon context */ 059 protected Context _context; 060 061 @Override 062 public void service(ServiceManager manager) throws ServiceException 063 { 064 _solrIndexer = (SolrIndexer) manager.lookup(SolrIndexer.ROLE); 065 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 066 _pageDAO = (PageDAO) manager.lookup(PageDAO.ROLE); 067 } 068 069 @Override 070 public void contextualize(Context context) throws ContextException 071 { 072 _context = context; 073 } 074 075 @Override 076 public boolean supports(Event event) 077 { 078 return event.getId().equals(ObservationConstants.EVENT_ZONEITEM_DELETED) 079 || event.getId().equals(ObservationConstants.EVENT_ZONEITEM_ADDED) 080 || event.getId().equals(ObservationConstants.EVENT_ZONEITEM_MODIFIED) 081 || event.getId().equals(ObservationConstants.EVENT_PAGE_CHANGED) 082 || event.getId().equals(ObservationConstants.EVENT_PAGE_MOVED) 083 || event.getId().equals(ObservationConstants.EVENT_PAGE_DELETED); 084 } 085 086 @Override 087 public int getPriority(Event event) 088 { 089 // Will be processed AFTER live synchronization observers, to have up-to-date pages in live, in order to have correct indexation data 090 return Observer.MAX_PRIORITY + 4000; 091 } 092 093 @Override 094 public void observe(Event event, Map<String, Object> transientVars) throws Exception 095 { 096 if (ObserverHelper.isNotSuspendedObservationForIndexation()) 097 { 098 if (event.getId().equals(ObservationConstants.EVENT_PAGE_CHANGED) || event.getId().equals(ObservationConstants.EVENT_PAGE_MOVED)) 099 { 100 Page page = (Page) event.getArguments().get(ObservationConstants.ARGS_PAGE); 101 if (page != null) 102 { 103 boolean pageWasInLive = _pageWasInLive(transientVars); 104 for (Content content : _pageDAO.getPageContents(page)) 105 { 106 _updateContentProperties(content, page.getId(), pageWasInLive); 107 } 108 } 109 } 110 else if (event.getId().equals(ObservationConstants.EVENT_PAGE_DELETED)) 111 { 112 @SuppressWarnings("unchecked") 113 List<Content> contents = (List<Content>) event.getArguments().get(ObservationConstants.ARGS_PAGE_CONTENTS); 114 String pageId = (String) event.getArguments().get(ObservationConstants.ARGS_PAGE_ID); 115 if (contents != null) 116 { 117 boolean pageWasInLive = _pageWasInLive(transientVars); 118 for (Content content : contents) 119 { 120 _updateContentProperties(content, pageId, pageWasInLive); 121 } 122 } 123 } 124 else 125 { 126 ZoneType zoneType = (ZoneType) event.getArguments().get(ObservationConstants.ARGS_ZONE_TYPE); 127 if (zoneType == ZoneType.CONTENT) 128 { 129 Content content = (Content) event.getArguments().get(ObservationConstants.ARGS_ZONE_ITEM_CONTENT); 130 if (content != null) 131 { 132 _updateContentProperties(content, null, false); 133 } 134 } 135 } 136 } 137 } 138 139 private boolean _pageWasInLive(Map<String, Object> transientVars) 140 { 141 return (boolean) transientVars.getOrDefault(ContentOrphanStatusPart1Observer.PAGE_WAS_IN_LIVE_KEY, false); 142 } 143 144 /** 145 * Update content properties 146 * @param content the content 147 * @param pageId the page id 148 * @param pageWasInLive if the page was in live before operation 149 * @throws Exception if an error occurred 150 */ 151 protected void _updateContentProperties(Content content, String pageId, boolean pageWasInLive) throws Exception 152 { 153 // default workspace 154 _updateProperties(content, RepositoryConstants.DEFAULT_WORKSPACE); 155 156 // live workspace 157 Request request = ContextHelper.getRequest(_context); 158 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 159 try 160 { 161 // Force the workspace. 162 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE); 163 164 if (_resolver.hasAmetysObjectForId(content.getId())) 165 { 166 Content liveContent = _resolver.resolveById(content.getId(), null); 167 if (StringUtils.isBlank(pageId)) 168 { 169 _updateProperties(liveContent, WebConstants.LIVE_WORKSPACE); 170 } 171 else 172 { 173 boolean isPageInLive = _resolver.hasAmetysObjectForId(pageId); 174 if (isPageInLive ^ pageWasInLive) // A XOR B : we update content properties in live workspace if page status has changed 175 { 176 _updateProperties(liveContent, WebConstants.LIVE_WORKSPACE); 177 } 178 } 179 } 180 } 181 finally 182 { 183 // Restore workspace 184 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 185 } 186 } 187 188 private void _updateProperties(Content content, String workspace) throws Exception 189 { 190 if (content instanceof WebContent) 191 { 192 // Update orphan status 193 _solrIndexer.updateSystemProperty(content, "orphan", workspace); 194 // Update pageIds 195 _solrIndexer.updateSystemProperty(content, "pages", workspace); 196 } 197 } 198}