From 52e76af7a6cd984493a150dee79b51a2b02f8fac Mon Sep 17 00:00:00 2001 From: Olmo del Corral Date: Tue, 16 Jul 2019 20:05:14 +0200 Subject: [PATCH 1/2] add QuickLink isShy to alert QuickLinkWidget --- Signum.React/Scripts/Operations.tsx | 3 ++- Signum.React/Scripts/QuickLinks.tsx | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Signum.React/Scripts/Operations.tsx b/Signum.React/Scripts/Operations.tsx index 3c59e5ee81..cff3d48f57 100644 --- a/Signum.React/Scripts/Operations.tsx +++ b/Signum.React/Scripts/Operations.tsx @@ -32,7 +32,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, })); } diff --git a/Signum.React/Scripts/QuickLinks.tsx b/Signum.React/Scripts/QuickLinks.tsx index dc8b778c93..301ae04d47 100644 --- a/Signum.React/Scripts/QuickLinks.tsx +++ b/Signum.React/Scripts/QuickLinks.tsx @@ -155,7 +155,7 @@ export function QuickLinkWidget(p: QuickLinkWidgetProps) { !l.isShy) ? "badge-warning" : "badge-light", "sf-quicklinks")} title={TitleManager.useTitle ? QuickLinkMessage.Quicklinks.niceToString() : undefined} role="button" href="#" @@ -179,6 +179,7 @@ export interface QuickLinkOptions { order?: number; icon?: IconProp; iconColor?: string; + isShy?: boolean; } export abstract class QuickLink { @@ -188,6 +189,7 @@ export abstract class QuickLink { name: string; icon?: IconProp; iconColor?: string; + isShy?: string; constructor(name: string, options?: QuickLinkOptions) { this.name = name; From d8be18a1f8517a02d7522933b032e4367c7b3593 Mon Sep 17 00:00:00 2001 From: Olmo del Corral Date: Thu, 18 Jul 2019 08:50:54 +0200 Subject: [PATCH 2/2] hide operations when isReadonly (except if showOnReadOnly is true) --- Signum.Engine/Operations/OperationLogic.cs | 2 +- .../ApiControllers/OperationController.cs | 12 ++++-- Signum.React/Scripts/Operations.tsx | 11 +++++ .../Operations/ContextualOperations.tsx | 42 ++++++++++++++----- .../Scripts/Operations/EntityOperations.tsx | 3 ++ 5 files changed, 55 insertions(+), 15 deletions(-) diff --git a/Signum.Engine/Operations/OperationLogic.cs b/Signum.Engine/Operations/OperationLogic.cs index 47d71b20f9..9fdb6ee48f 100644 --- a/Signum.Engine/Operations/OperationLogic.cs +++ b/Signum.Engine/Operations/OperationLogic.cs @@ -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) diff --git a/Signum.React/ApiControllers/OperationController.cs b/Signum.React/ApiControllers/OperationController.cs index d565443e17..a2c24e7413 100644 --- a/Signum.React/ApiControllers/OperationController.cs +++ b/Signum.React/ApiControllers/OperationController.cs @@ -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[], bool> AnyReadonly; + #pragma warning disable CS8618 // Non-nullable field is uninitialized. public class StateCanExecuteRequest { @@ -237,9 +244,8 @@ public StateCanExecuteResponse(Dictionary canExecutes) this.canExecutes = canExecutes; } + public bool anyReadonly; public Dictionary canExecutes { get; set; } } - - } } diff --git a/Signum.React/Scripts/Operations.tsx b/Signum.React/Scripts/Operations.tsx index cff3d48f57..1c7593539f 100644 --- a/Signum.React/Scripts/Operations.tsx +++ b/Signum.React/Scripts/Operations.tsx @@ -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); @@ -157,6 +162,7 @@ export class ContextualOperationSettings extends OperationSett isVisible?: (coc: ContextualOperationContext) => boolean; hideOnCanExecute?: boolean; + showOnReadOnly?: boolean; confirmMessage?: (coc: ContextualOperationContext) => string | undefined | null; onClick?: (coc: ContextualOperationContext) => void; color?: BsColor; @@ -175,6 +181,7 @@ export interface ContextualOperationOptions { text?: () => string; isVisible?: (coc: ContextualOperationContext) => boolean; hideOnCanExecute?: boolean; + showOnReadOnly?: boolean; confirmMessage?: (coc: ContextualOperationContext) => string | undefined | null; onClick?: (coc: ContextualOperationContext) => void; color?: BsColor; @@ -189,6 +196,7 @@ export class ContextualOperationContext { settings?: ContextualOperationSettings; entityOperationSettings?: EntityOperationSettings; canExecute?: string; + isReadonly?: boolean; event?: React.MouseEvent; onContextualSuccess?: (pack: API.ErrorReport) => void; onConstructFromSuccess?: (pack: EntityPack) => void; @@ -327,6 +335,7 @@ export class EntityOperationSettings extends OperationSettings confirmMessage?: (eoc: EntityOperationContext) => string | undefined | null; onClick?: (eoc: EntityOperationContext) => void; hideOnCanExecute?: boolean; + showOnReadOnly?: boolean; group?: EntityOperationGroup | null; order?: number; color?: BsColor; @@ -356,6 +365,7 @@ export interface EntityOperationOptions { confirmMessage?: (ctx: EntityOperationContext) => string | undefined | null; onClick?: (ctx: EntityOperationContext) => void; hideOnCanExecute?: boolean; + showOnReadOnly?: boolean; group?: EntityOperationGroup | null; order?: number; color?: BsColor; @@ -572,6 +582,7 @@ export namespace API { export interface CanExecutesResponse { canExecutes: { [operationKey: string]: string }; + isReadOnly?: boolean; } } diff --git a/Signum.React/Scripts/Operations/ContextualOperations.tsx b/Signum.React/Scripts/Operations/ContextualOperations.tsx index 554890e68b..df1be34881 100644 --- a/Signum.React/Scripts/Operations/ContextualOperations.tsx +++ b/Signum.React/Scripts/Operations/ContextualOperations.tsx @@ -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): Promise | undefined { @@ -107,24 +108,33 @@ export function getEntityOperationsContextualItems(ctx: ContextualItemsContext[]>; - 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)); @@ -151,6 +161,16 @@ function hideOnCanExecute(coc: ContextualOperationContext) { } +function showOnReadonly(coc: ContextualOperationContext) { + 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): Promise { diff --git a/Signum.React/Scripts/Operations/EntityOperations.tsx b/Signum.React/Scripts/Operations/EntityOperations.tsx index 1c4d0f88b8..d6ff541be0 100644 --- a/Signum.React/Scripts/Operations/EntityOperations.tsx +++ b/Signum.React/Scripts/Operations/EntityOperations.tsx @@ -53,6 +53,9 @@ export function getEntityOperationButtons(ctx: ButtonsContext): Array eoc!);