Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
olmobrutall committed Jul 18, 2019
2 parents 235df9c + d8be18a commit 07dc22f
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Signum.Engine/Operations/OperationLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ public static OperationType OperationType(Type type, OperationSymbol operationSy
{
var operations = operationSymbols.Select(opKey => FindOperation(grLites.Key, opKey)).ToList();

foreach (var grOperations in operations.GroupBy(a => a.GetType().GetGenericArguments().Let(arr=>Tuple.Create(arr[0], arr[1]))))
foreach (var grOperations in operations.GroupBy(a => a.GetType().GetGenericArguments().Let(arr => Tuple.Create(arr[0], arr[1]))))
{
var dic = giGetContextualGraphCanExecute.GetInvoker(grLites.Key, grOperations.Key.Item1, grOperations.Key.Item2)(grLites, grOperations);
if (result == null)
Expand Down
12 changes: 9 additions & 3 deletions Signum.React/ApiControllers/OperationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,17 @@ public StateCanExecuteResponse StateCanExecutes([Required, FromBody]StateCanExec
.ToList();

var result = OperationLogic.GetContextualCanExecute(request.lites, operationSymbols)!;
var anyReadonly = AnyReadonly.GetInvocationListTyped().Any(f => f(request.lites));

return new StateCanExecuteResponse(result.SelectDictionary(a => a.Key, v => v));
return new StateCanExecuteResponse(result.SelectDictionary(a => a.Key, v => v))
{
anyReadonly = anyReadonly
};
}


public static Func<Lite<Entity>[], bool> AnyReadonly;

#pragma warning disable CS8618 // Non-nullable field is uninitialized.
public class StateCanExecuteRequest
{
Expand All @@ -237,9 +244,8 @@ public StateCanExecuteResponse(Dictionary<string, string> canExecutes)
this.canExecutes = canExecutes;
}

public bool anyReadonly;
public Dictionary<string, string> canExecutes { get; set; }
}


}
}
14 changes: 13 additions & 1 deletion Signum.React/Scripts/Operations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import { ContextualItemsContext } from './SearchControl/ContextualItems';
import { BsColor, KeyCodes } from "./Components/Basic";
import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { bool } from "prop-types";
export namespace Options {
export function maybeReadonly(ti: TypeInfo) {
return false;
}
}

export function start() {
ButtonBar.onButtonBarRender.push(getEntityOperationButtons);
Expand All @@ -32,7 +37,8 @@ export function start() {
{
isVisible: getTypeInfo(ctx.lite.EntityType) && getTypeInfo(ctx.lite.EntityType).requiresSaveOperation && Finder.isFindable(OperationLogEntity, false),
icon: "history",
iconColor: "green"
iconColor: "green",
isShy: true,
}));
}

Expand Down Expand Up @@ -156,6 +162,7 @@ export class ContextualOperationSettings<T extends Entity> extends OperationSett

isVisible?: (coc: ContextualOperationContext<T>) => boolean;
hideOnCanExecute?: boolean;
showOnReadOnly?: boolean;
confirmMessage?: (coc: ContextualOperationContext<T>) => string | undefined | null;
onClick?: (coc: ContextualOperationContext<T>) => void;
color?: BsColor;
Expand All @@ -174,6 +181,7 @@ export interface ContextualOperationOptions<T extends Entity> {
text?: () => string;
isVisible?: (coc: ContextualOperationContext<T>) => boolean;
hideOnCanExecute?: boolean;
showOnReadOnly?: boolean;
confirmMessage?: (coc: ContextualOperationContext<T>) => string | undefined | null;
onClick?: (coc: ContextualOperationContext<T>) => void;
color?: BsColor;
Expand All @@ -188,6 +196,7 @@ export class ContextualOperationContext<T extends Entity> {
settings?: ContextualOperationSettings<T>;
entityOperationSettings?: EntityOperationSettings<T>;
canExecute?: string;
isReadonly?: boolean;
event?: React.MouseEvent<any>;
onContextualSuccess?: (pack: API.ErrorReport) => void;
onConstructFromSuccess?: (pack: EntityPack<Entity>) => void;
Expand Down Expand Up @@ -325,6 +334,7 @@ export class EntityOperationSettings<T extends Entity> extends OperationSettings
confirmMessage?: (eoc: EntityOperationContext<T>) => string | undefined | null;
onClick?: (eoc: EntityOperationContext<T>) => void;
hideOnCanExecute?: boolean;
showOnReadOnly?: boolean;
group?: EntityOperationGroup | null;
order?: number;
color?: BsColor;
Expand Down Expand Up @@ -354,6 +364,7 @@ export interface EntityOperationOptions<T extends Entity> {
confirmMessage?: (ctx: EntityOperationContext<T>) => string | undefined | null;
onClick?: (ctx: EntityOperationContext<T>) => void;
hideOnCanExecute?: boolean;
showOnReadOnly?: boolean;
group?: EntityOperationGroup | null;
order?: number;
color?: BsColor;
Expand Down Expand Up @@ -570,6 +581,7 @@ export namespace API {

export interface CanExecutesResponse {
canExecutes: { [operationKey: string]: string };
isReadOnly?: boolean;
}
}

Expand Down
42 changes: 31 additions & 11 deletions Signum.React/Scripts/Operations/ContextualOperations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '../Operations'
import { DropdownItem } from "../Components/DropdownItem";
import { UncontrolledTooltip } from "../Components/Tooltip";
import * as Operations from "../Operations";
import { IconProp } from "@fortawesome/fontawesome-svg-core";

export function getConstructFromManyContextualItems(ctx: ContextualItemsContext<Entity>): Promise<MenuItemBlock | undefined> | undefined {
Expand Down Expand Up @@ -109,24 +110,33 @@ export function getEntityOperationsContextualItems(ctx: ContextualItemsContext<E
return undefined;

let contextPromise: Promise<ContextualOperationContext<Entity>[]>;
if (ctx.lites.length == 1 && contexts.some(coc => coc.operationInfo.hasCanExecute)) {
contextPromise = Navigator.API.fetchEntityPack(ctx.lites[0]).then(ep => {
contexts.forEach(coc => coc.canExecute = ep.canExecute[coc.operationInfo.key]);
return contexts;
});
} else if (ctx.lites.length > 1 && contexts.some(coc => coc.operationInfo.hasStates)) {
contextPromise = API.stateCanExecutes(ctx.lites, contexts.filter(coc => coc.operationInfo.hasStates).map(a => a.operationInfo.key))
.then(response => {
contexts.forEach(coc => coc.canExecute = response.canExecutes[coc.operationInfo.key]);
if (contexts.some(coc => coc.operationInfo.hasCanExecute) || Operations.Options.maybeReadonly(ti)) {
if (ctx.lites.length == 1) {
contextPromise = Navigator.API.fetchEntityPack(ctx.lites[0]).then(ep => {
contexts.forEach(coc => {
coc.canExecute = ep.canExecute[coc.operationInfo.key];
coc.isReadonly = Navigator.isReadOnly(ep);
});
return contexts;
});
} else /*if (ctx.lites.length > 1)*/ {
contextPromise = API.stateCanExecutes(ctx.lites, contexts.filter(coc => coc.operationInfo.hasStates).map(a => a.operationInfo.key))
.then(response => {
contexts.forEach(coc => {
coc.canExecute = response.canExecutes[coc.operationInfo.key];
coc.isReadonly = response.isReadOnly;
});
return contexts;
});
}
} else {
contextPromise = Promise.resolve(contexts);
}


return contextPromise.then(ctxs => {
const menuItems = ctxs.filter(coc => coc.canExecute == undefined || !hideOnCanExecute(coc))
const menuItems = ctxs
.filter(coc => coc.canExecute == undefined || !hideOnCanExecute(coc))
.filter(coc => !coc.isReadonly || showOnReadonly(coc))
.orderBy(coc => coc.settings && coc.settings.order != undefined ? coc.settings.order :
coc.entityOperationSettings && coc.entityOperationSettings.order != undefined ? coc.entityOperationSettings.order : 0)
.flatMap(coc => MenuItemConstructor.createContextualMenuItem(coc, defaultContextualClick));
Expand All @@ -153,6 +163,16 @@ function hideOnCanExecute(coc: ContextualOperationContext<Entity>) {
}


function showOnReadonly(coc: ContextualOperationContext<Entity>) {
if (coc.settings && coc.settings.showOnReadOnly != undefined)
return coc.settings.showOnReadOnly;

if (coc.entityOperationSettings && coc.entityOperationSettings.showOnReadOnly != undefined)
return coc.entityOperationSettings.showOnReadOnly;

return false;
}


export function confirmInNecessary(coc: ContextualOperationContext<Entity>): Promise<boolean> {

Expand Down
3 changes: 3 additions & 0 deletions Signum.React/Scripts/Operations/EntityOperations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ export function getEntityOperationButtons(ctx: ButtonsContext): Array<ButtonBarE
if (eos && eos.hideOnCanExecute && eoc.canExecute)
return false;

if (Navigator.isReadOnly(ctx.pack) && !(eos && eos.showOnReadOnly))
return false;

return true;
})
.map(eoc => eoc!);
Expand Down
4 changes: 3 additions & 1 deletion Signum.React/Scripts/QuickLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export function QuickLinkWidget(p: QuickLinkWidgetProps) {
<UncontrolledDropdown id="quickLinksWidget">
<DropdownToggle tag="span" data-toggle="dropdown">
<a
className={classes("badge badge-secondary badge-pill", "sf-widgets-active", "sf-quicklinks")}
className={classes("badge badge-pill", links && links.some(l => !l.isShy) ? "badge-warning" : "badge-light", "sf-quicklinks")}
title={TitleManager.useTitle ? QuickLinkMessage.Quicklinks.niceToString() : undefined}
role="button"
href="#"
Expand All @@ -179,6 +179,7 @@ export interface QuickLinkOptions {
order?: number;
icon?: IconProp;
iconColor?: string;
isShy?: boolean;
}

export abstract class QuickLink {
Expand All @@ -188,6 +189,7 @@ export abstract class QuickLink {
name: string;
icon?: IconProp;
iconColor?: string;
isShy?: string;

constructor(name: string, options?: QuickLinkOptions) {
this.name = name;
Expand Down

0 comments on commit 07dc22f

Please sign in to comment.