Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.isis.applib.annotation.ActionLayout;
import org.apache.isis.applib.annotation.ActionSemantics;
import org.apache.isis.applib.annotation.Bulk;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.applib.annotation.Where;
import org.apache.isis.applib.filter.Filter;
import org.apache.isis.applib.value.Blob;
Expand Down Expand Up @@ -221,6 +222,22 @@ public static String nameFor(final ObjectAction objAction) {
return "(no name)";
}

public static SemanticsOf semanticsOf(final ObjectAction objectAction) {
return SemanticsOf.from(objectAction.getSemantics());
}

public static boolean isAreYouSureSemantics(final ObjectAction objectAction) {
return semanticsOf(objectAction).isAreYouSure();
}

public static boolean isNonIdempotent(ObjectAction objectAction) {
return !semanticsOf(objectAction).isIdempotentInNature();
}

public static boolean isNoParameters(ObjectAction objectAction) {
return objectAction.getParameterCount()==0;
}

public static boolean returnsBlobOrClob(final ObjectAction objectAction) {
final ObjectSpecification returnType = objectAction.getReturnType();
if (returnType != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ public Builder describedAs(String descriptionIfAny) {
cssMenuItem.setDescription(descriptionIfAny);
return this;
}

/**
* @see CssMenuItem#requiresImmediateConfirmation
* @param requiresImmediateConfirmation
* @return
*/
public Builder requiresImmediateConfirmation(boolean requiresImmediateConfirmation) {
cssMenuItem.setRequiresImmediateConfirmation(requiresImmediateConfirmation);
return this;
}

public Builder returnsBlobOrClob(boolean blobOrClob) {
cssMenuItem.setReturnsBlobOrClob(blobOrClob);
Expand Down Expand Up @@ -150,6 +160,7 @@ public CssMenuItem build() {
private AbstractLink link;
private boolean enabled = true; // unless disabled
private String disabledReason;
private boolean requiresImmediateConfirmation = false; // unless set otherwise
private boolean blobOrClob = false; // unless set otherwise
private boolean prototype = false; // unless set otherwise
private boolean requiresSeparator = false; // unless set otherwise
Expand Down Expand Up @@ -252,6 +263,28 @@ public boolean isEnabled() {
private void setEnabled(final boolean enabled) {
this.enabled = enabled;
}

/**
* A menu action with no parameters AND an are-you-sure semantics
* does require an immediate confirmation dialog.
* <br/>
* Others don't.
* @return
*/
public boolean requiresImmediateConfirmation() {
return requiresImmediateConfirmation;
}

/**
* A menu action with no parameters AND an are-you-sure semantics
* does require an immediate confirmation dialog.
* <br/>
* Others don't.
* @param requiresImmediateConfirmation
*/
public void setRequiresImmediateConfirmation(boolean requiresImmediateConfirmation) {
this.requiresImmediateConfirmation = requiresImmediateConfirmation;
}

public void setReturnsBlobOrClob(boolean blobOrClob) {
this.blobOrClob = blobOrClob;
Expand Down Expand Up @@ -310,7 +343,7 @@ public void setDescription(String description) {
*/
Builder newSubMenuItem(ServiceAndAction serviceAndAction) {

final EntityModel targetEntityModel = serviceAndAction.serviceEntityModel;
final EntityModel targetEntityModel = serviceAndAction.serviceEntityModel;
final ObjectAction objectAction = serviceAndAction.objectAction;
final boolean separator = serviceAndAction.separator;
final ServiceActionLinkFactory actionLinkFactory = serviceAndAction.linkAndLabelFactory;
Expand Down Expand Up @@ -355,6 +388,9 @@ Builder newSubMenuItem(ServiceAndAction serviceAndAction) {
.link(link)
.describedAs(descriptionIfAny)
.enabled(reasonDisabledIfAny)
.requiresImmediateConfirmation(
ObjectAction.Util.isAreYouSureSemantics(objectAction) &&
ObjectAction.Util.isNoParameters(objectAction))
.returnsBlobOrClob(ObjectAction.Util.returnsBlobOrClob(objectAction))
.prototyping(objectAction.isPrototype())
.requiresSeparator(separator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,41 +28,51 @@
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.Model;

import org.apache.isis.applib.annotation.ActionSemantics;
import org.apache.isis.applib.annotation.NatureOfService;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.applib.filter.Filters;
import org.apache.isis.applib.services.i18n.TranslationService;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
import org.apache.isis.core.metamodel.facets.actions.notinservicemenu.NotInServiceMenuFacet;
import org.apache.isis.core.metamodel.facets.all.named.NamedFacet;
import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
import org.apache.isis.core.metamodel.services.ServicesInjector;
import org.apache.isis.core.metamodel.spec.ActionType;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.metamodel.spec.feature.Contributed;
import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
import org.apache.isis.core.runtime.system.IsisSystem;
import org.apache.isis.core.runtime.system.session.IsisSessionFactoryBuilder;
import org.apache.isis.viewer.wicket.model.models.EntityModel;
import org.apache.isis.viewer.wicket.model.models.ServiceActionsModel;
import org.apache.isis.viewer.wicket.ui.components.actionmenu.CssClassFaBehavior;
import org.apache.isis.viewer.wicket.ui.panels.PanelUtil;
import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;

import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipBehavior;
import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationBehavior;
import de.agilecoders.wicket.extensions.markup.html.bootstrap.confirmation.ConfirmationConfig;

public final class ServiceActionUtil {

private ServiceActionUtil(){}



static void addLeafItem(final CssMenuItem menuItem, final ListItem<CssMenuItem> listItem, final MarkupContainer parent) {
Fragment leafItem;

Fragment leafItem;
if (!menuItem.isSeparator()) {
leafItem = new Fragment("content", "leafItem", parent);

Expand All @@ -76,6 +86,24 @@ static void addLeafItem(final CssMenuItem menuItem, final ListItem<CssMenuItem>
subMenuItemLink.setEnabled(false);
TooltipBehavior tooltipBehavior = new TooltipBehavior(Model.of(menuItem.getDisabledReason()));
listItem.add(tooltipBehavior);
} else {

if(!Strings.isNullOrEmpty(menuItem.getDescription())) {
//XXX ISIS-1625, tooltips for menu actions
listItem.add(new AttributeModifier("title", Model.of(menuItem.getDescription())));

// ISIS-1615, prevent bootstrap from changing the HTML link's 'title' attribute on client-side;
// bootstrap will not touch the 'title' attribute once the HTML link has a 'data-original-title' attribute
subMenuItemLink.add(new AttributeModifier("data-original-title", ""));
}

//XXX ISIS-1626, confirmation dialog for no-parameter menu actions
if (menuItem.requiresImmediateConfirmation()) {
addConfirmationDialog(
subMenuItemLink,
menuItem.getPersistenceSession().getServicesInjector());
}

}
if (menuItem.isPrototyping()) {
subMenuItemLink.add(new CssClassAppender("prototype"));
Expand Down Expand Up @@ -307,5 +335,28 @@ private static Map<String, List<ServiceAndAction>> groupByServiceName(final List

return serviceActionsByName;
}

private static void addConfirmationDialog(Component component, final ServicesInjector servicesInjector) {

final TranslationService translationService =
servicesInjector.lookupService(TranslationService.class);

ConfirmationConfig confirmationConfig = new ConfirmationConfig();

final String context = IsisSessionFactoryBuilder.class.getName();
final String areYouSure = translationService.translate(context, IsisSystem.MSG_ARE_YOU_SURE);
final String confirm = translationService.translate(context, IsisSystem.MSG_CONFIRM);
final String cancel = translationService.translate(context, IsisSystem.MSG_CANCEL);

confirmationConfig
.withTitle(areYouSure)
.withBtnOkLabel(confirm)
.withBtnCancelLabel(cancel)
.withPlacement(TooltipConfig.Placement.bottom)
.withBtnOkClass("btn btn-danger")
.withBtnCancelClass("btn btn-default");

component.add(new ConfirmationBehavior(null, confirmationConfig));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
import org.apache.wicket.extensions.ajax.markup.html.AjaxIndicatorAppender;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.request.IRequestHandler;

import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
import org.apache.wicket.util.time.Duration;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
Expand Down Expand Up @@ -203,7 +205,14 @@ protected IRequestHandler getRequestHandler() {
protected IRequestHandler getRequestHandler() {
final ObjectAdapter resultAdapter = actionModel.execute();
final Object value = resultAdapter.getObject();
return ActionModel.downloadHandler(value);

final IRequestHandler handler = ActionModel.downloadHandler(value);

//XXX ISIS-1619, prevent clients from caching the response content
return isNonIdempotent(actionModel)
? enforceNoCacheOnClientSide(handler)
: handler
;
}
};
}
Expand All @@ -223,5 +232,26 @@ private static boolean isNoArgReturnTypeDownload(final ObjectAction action) {
(action.getReturnType().getCorrespondingClass() == org.apache.isis.applib.value.Blob.class ||
action.getReturnType().getCorrespondingClass() == org.apache.isis.applib.value.Clob.class);
}

private static boolean isNonIdempotent(ActionModel actionModel) {
final ObjectAction objectAction = actionModel.getActionMemento()
.getAction(actionModel.getSpecificationLoader());
return !SemanticsOf.from(objectAction.getSemantics()).isIdempotentInNature();
}

// -- CLIENT SIDE CACHING ASPECTS ...

private static IRequestHandler enforceNoCacheOnClientSide(IRequestHandler downloadHandler){
if(downloadHandler==null)
return downloadHandler;

if(downloadHandler instanceof ResourceStreamRequestHandler)
((ResourceStreamRequestHandler) downloadHandler)
.setCacheDuration(Duration.seconds(0));

return downloadHandler;
}

// --

}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ public ActionPromptHeaderPanel call() throws Exception {
}



protected LinkAndLabel newLinkAndLabel(
final ObjectAdapter objectAdapter,
final ObjectAction objectAction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -951,3 +951,9 @@ such as https://chrome.google.com/webstore/detail/focus-indicator/heeoeadndnhebm
#container.page #wicketDebugBar span#wicketDebugBarContents a#wicketDebugBarRemove img {
margin-top: 2px;
}

/* ISIS-1626, confirmation dialog for no-parameter menu actions
allow confirmation dialog popup to escape boundaries of parent html li element*/
.open > .dropdown-menu {
overflow: visible;
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,14 @@ public static void addConfirmationDialogIfAreYouSureSemantics(
final Component component,
final SemanticsOf semanticsOf,
final ServicesInjector servicesInjector) {
final TranslationService translationService =
servicesInjector.lookupService(TranslationService.class);

if (!semanticsOf.isAreYouSure()) {
return;
}

final TranslationService translationService =
servicesInjector.lookupService(TranslationService.class);

ConfirmationConfig confirmationConfig = new ConfirmationConfig();

final String context = IsisSessionFactoryBuilder.class.getName();
Expand Down