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