Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion admin-console/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.entando</groupId>
<artifactId>app-engine</artifactId>
<version>7.5.2</version>
<version>7.5.3-SNAPSHOT</version>
</parent>
<groupId>org.entando.entando</groupId>
<artifactId>entando-admin-console</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion cds-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.entando</groupId>
<artifactId>app-engine</artifactId>
<version>7.5.2</version>
<version>7.5.3-SNAPSHOT</version>
</parent>
<artifactId>entando-plugin-jpcds</artifactId>
<groupId>org.entando.entando.plugins</groupId>
Expand Down
2 changes: 1 addition & 1 deletion cms-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.entando</groupId>
<artifactId>app-engine</artifactId>
<version>7.5.2</version>
<version>7.5.3-SNAPSHOT</version>
</parent>
<groupId>org.entando.entando.plugins</groupId>
<artifactId>entando-plugin-jacms</artifactId>
Expand Down
6 changes: 5 additions & 1 deletion contentscheduler-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.entando</groupId>
<artifactId>app-engine</artifactId>
<version>7.5.2</version>
<version>7.5.3-SNAPSHOT</version>
</parent>
<artifactId>entando-plugin-jpcontentscheduler</artifactId>
<groupId>org.entando.entando.plugins</groupId>
Expand Down Expand Up @@ -154,6 +154,10 @@
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import com.agiletec.aps.system.common.AbstractService;
import com.agiletec.aps.system.common.entity.model.EntitySearchFilter;
import com.agiletec.aps.system.common.entity.model.attribute.ITextAttribute;
import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.aps.system.services.authorization.IApsAuthority;
import com.agiletec.aps.system.services.authorization.IAuthorizationManager;
import com.agiletec.aps.system.services.baseconfig.ConfigInterface;
Expand All @@ -38,11 +37,13 @@
import com.agiletec.plugins.jacms.aps.system.services.cache.CmsCacheWrapperManager;
import com.agiletec.plugins.jacms.aps.system.services.content.IContentManager;
import com.agiletec.plugins.jacms.aps.system.services.content.IContentSearcherDAO;
import com.agiletec.plugins.jacms.aps.system.services.content.event.PublicContentChangedEvent;
import com.agiletec.plugins.jacms.aps.system.services.content.model.Content;
import com.agiletec.plugins.jpmail.aps.services.mail.IMailManager;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -445,6 +446,10 @@ public void moveOnLineContent(Content content, boolean updateDate, boolean updat
} else {
this.getContentSchedulerDAO().updateContent(content, updateDate);
this.getContentSchedulerDAO().publishContent(content);
int operationEventCode = content.isOnLine()
? PublicContentChangedEvent.UPDATE_OPERATION_CODE
: PublicContentChangedEvent.INSERT_OPERATION_CODE;
this.notifyPublicContentChanging(content, operationEventCode);
}
} catch (Throwable t) {
ApsSystemUtils.logThrowable(t, this, "moveOnLineContent");
Expand Down Expand Up @@ -472,15 +477,35 @@ public void removeOnLineContent(Content content, boolean updateLastModified) thr
content.setStatus(Content.STATUS_READY);
}
this.getContentSchedulerDAO().unpublishOnLineContent(content);
// this.notifyPublicContentChanging(content,
// PublicContentChangedEvent.REMOVE_OPERATION_CODE);
this.notifyPublicContentChanging(content, PublicContentChangedEvent.REMOVE_OPERATION_CODE);
this.flushGroups(content.getId(), content.getTypeCode());
} catch (Throwable t) {
ApsSystemUtils.logThrowable(t, this, "removeOnLineContent");
throw new EntException("Error while removing onLine content", t);
}
}

/**
* Notify the change of a published content to the observing services.
* The event is built with the same channel and the same properties
* used by ContentManager, so that it is propagated to the
* other instances as well.
*
* @param content The changed content.
* @param operationCode The code of the operation to notify.
*/
private void notifyPublicContentChanging(Content content, int operationCode) {
Map<String, String> properties = new HashMap<>();
properties.put("contentId", content.getId());
properties.put("operationCode", String.valueOf(operationCode));
PublicContentChangedEvent event = new PublicContentChangedEvent(JacmsSystemConstants.CONTENT_EVENT_CHANNEL, properties);
// setContent is deprecated but must still be set: SeoMappingManager discards the event
// when getContent() is null, while the search engine only needs the contentId.
event.setContent(content);
event.setOperationCode(operationCode);
this.notifyEvent(event);
}

private void flushGroups(String contentId, String typeCode) {
String[] groups = (null != typeCode) ? CmsCacheWrapperManager.getContentCacheGroupsToEvict(contentId, typeCode) : CmsCacheWrapperManager.getContentCacheGroupsToEvict(contentId);
for (int i = 0; i < groups.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
<bean id="jpcontentschedulerContentSchedulerManager"
class="org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.ContentSchedulerManager" parent="abstractService">
<property name="contentSchedulerDAO" >
<bean class="org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.ContentSchedulerDAO">
<bean class="org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content.ContentSchedulerDAO"
parent="abstractEntityDAO">
<property name="dataSource" ref="portDataSource" />
<property name="categoryManager" ref="CategoryManager" />
</bean>
</property>
<property name="workContentSearcherDAO" ref="jacmsWorkContentSearcherDAO" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@
<div class="col-sm-10">
<div class="checkbox">
<wpsf:checkbox name="contentTypeElem.suspend" id="contentTypeElem_suspend" cssClass=" bootstrap-switch" />
<%-- An unchecked box would submit nothing and leave
'suspend' null instead of "false". The marker below lets the 'checkbox'
interceptor inject the unchecked value. --%>
<input type="hidden" name="__checkbox_contentTypeElem.suspend" value="false" />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Copyright 2017-Present Entando Inc. (http://www.entando.com) All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.entando.entando.plugins.jpcontentscheduler.aps.system.services.content;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.agiletec.aps.system.common.notify.INotifyManager;
import com.agiletec.aps.system.services.keygenerator.IKeyGeneratorManager;
import com.agiletec.plugins.jacms.aps.system.JacmsSystemConstants;
import com.agiletec.plugins.jacms.aps.system.services.content.event.PublicContentChangedEvent;
import com.agiletec.plugins.jacms.aps.system.services.content.model.Content;
import org.entando.entando.aps.system.services.cache.ICacheInfoManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationEvent;

/**
* The scheduler publishes contents through the core ContentManager, which notifies the
* PublicContentChangedEvent on its own, but it unpublishes and archives them through its
* own manager: these tests cover the notifications that manager has to send by itself,
* otherwise the search engine indexes keep the unpublished contents (ECS-596).
*/
@ExtendWith(MockitoExtension.class)
class ContentSchedulerManagerTest {

private static final String CONTENT_ID = "ART123";
private static final String TYPE_CODE = "ART";

@Mock
private IContentSchedulerDAO contentSchedulerDAO;
@Mock
private INotifyManager notifyManager;
@Mock
private ICacheInfoManager cacheInfoManager;
@Mock
private IKeyGeneratorManager keyGeneratorManager;

@Captor
private ArgumentCaptor<ApplicationEvent> eventCaptor;

private ContentSchedulerManager contentSchedulerManager;

@BeforeEach
void setUp() {
this.contentSchedulerManager = new ContentSchedulerManager();
this.contentSchedulerManager.setContentSchedulerDAO(this.contentSchedulerDAO);
this.contentSchedulerManager.setNotifyManager(this.notifyManager);
this.contentSchedulerManager.setCacheInfoManager(this.cacheInfoManager);
this.contentSchedulerManager.setKeyGeneratorManager(this.keyGeneratorManager);
}

@Test
void removeOnLineContentShouldNotifyTheRemoveOperation() throws Exception {
Content content = this.createContent(true, Content.STATUS_PUBLIC);

this.contentSchedulerManager.removeOnLineContent(content, false);

verify(this.contentSchedulerDAO).unpublishOnLineContent(content);
PublicContentChangedEvent event = this.captureNotifiedEvent();
assertEquals(PublicContentChangedEvent.REMOVE_OPERATION_CODE, event.getOperationCode());
assertEquals(CONTENT_ID, event.getContentId());
assertEquals(Content.STATUS_READY, content.getStatus());
}

@Test
void notifiedEventShouldBeAddressedToTheContentChannel() throws Exception {
Content content = this.createContent(true, Content.STATUS_PUBLIC);

this.contentSchedulerManager.removeOnLineContent(content, false);

// RedisNotifyManager republishes an event only when both channel and message are
// set, so a bare "new PublicContentChangedEvent()" would never leave the instance.
PublicContentChangedEvent event = this.captureNotifiedEvent();
assertEquals(JacmsSystemConstants.CONTENT_EVENT_CHANNEL, event.getChannel());
assertNotNull(event.getMessage());
assertTrue(event.getMessage().contains(CONTENT_ID));
assertTrue(event.getMessage().contains(String.valueOf(PublicContentChangedEvent.REMOVE_OPERATION_CODE)));
}

@Test
@SuppressWarnings("deprecation")
void notifiedEventShouldCarryTheContentObject() throws Exception {
Content content = this.createContent(true, Content.STATUS_PUBLIC);

this.contentSchedulerManager.removeOnLineContent(content, false);

// SeoMappingManager discards the event when getContent() is null, so the
// deprecated content object has to be set as ContentManager does.
assertEquals(content, this.captureNotifiedEvent().getContent());
}

@Test
void moveOnLineContentShouldNotifyTheUpdateOperationForAPublishedContent() throws Exception {
Content content = this.createContent(true, Content.STATUS_PUBLIC);

this.contentSchedulerManager.moveOnLineContent(content, false, false);

verify(this.contentSchedulerDAO).updateContent(content, false);
verify(this.contentSchedulerDAO).publishContent(content);
PublicContentChangedEvent event = this.captureNotifiedEvent();
assertEquals(PublicContentChangedEvent.UPDATE_OPERATION_CODE, event.getOperationCode());
assertEquals(CONTENT_ID, event.getContentId());
}

@Test
void moveOnLineContentShouldNotifyTheInsertOperationForAnUnpublishedContent() throws Exception {
Content content = this.createContent(false, Content.STATUS_READY);

this.contentSchedulerManager.moveOnLineContent(content, false, false);

assertEquals(PublicContentChangedEvent.INSERT_OPERATION_CODE,
this.captureNotifiedEvent().getOperationCode());
}

@Test
void moveOnLineContentShouldNotNotifyWhenTheContentIsJustCreated() throws Exception {
when(this.keyGeneratorManager.getUniqueKeyCurrentValue()).thenReturn(99);
Content content = this.createContent(false, Content.STATUS_READY);
content.setId(null);

this.contentSchedulerManager.moveOnLineContent(content, false, false);

// Nothing gets published here: the content is only added to the work version.
assertEquals(TYPE_CODE + "99", content.getId());
verify(this.contentSchedulerDAO).addEntity(content);
verify(this.notifyManager, never()).publishEvent(any());
}

private Content createContent(boolean onLine, String status) {
Content content = new Content();
content.setId(CONTENT_ID);
content.setTypeCode(TYPE_CODE);
content.setDescription("test content");
content.setStatus(status);
content.setOnLine(onLine);
return content;
}

private PublicContentChangedEvent captureNotifiedEvent() {
verify(this.notifyManager).publishEvent(this.eventCaptor.capture());
return assertInstanceOf(PublicContentChangedEvent.class, this.eventCaptor.getValue());
}

}
Loading
Loading