mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Support user variable in dropdown conditions
Summary: Dropdown conditions can now reference a `user` variable, similar to the one available in Access Rules. Test Plan: Browser test. Reviewers: jarek, paulfitz Reviewed By: jarek, paulfitz Differential Revision: https://phab.getgrist.com/D4255
This commit is contained in:
@@ -3,6 +3,7 @@ import {TableDataAction} from 'app/common/DocActions';
|
||||
import {FilteredDocUsageSummary} from 'app/common/DocUsage';
|
||||
import {Role} from 'app/common/roles';
|
||||
import {StringUnion} from 'app/common/StringUnion';
|
||||
import {UserInfo} from 'app/common/User';
|
||||
import {FullUser} from 'app/common/UserAPI';
|
||||
|
||||
// Possible flavors of items in a list of documents.
|
||||
@@ -75,6 +76,7 @@ export interface OpenLocalDocResult {
|
||||
doc: {[tableId: string]: TableDataAction};
|
||||
log: MinimalActionGroup[];
|
||||
isTimingOn: boolean;
|
||||
user: UserInfo;
|
||||
recoveryMode?: boolean;
|
||||
userOverride?: UserOverride;
|
||||
docUsage?: FilteredDocUsageSummary;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {PartialPermissionSet} from 'app/common/ACLPermissions';
|
||||
import {CellValue, RowRecord} from 'app/common/DocActions';
|
||||
import {CompiledPredicateFormula} from 'app/common/PredicateFormula';
|
||||
import {Role} from 'app/common/roles';
|
||||
import {MetaRowRecord} from 'app/common/TableData';
|
||||
|
||||
export interface RuleSet {
|
||||
@@ -25,12 +24,6 @@ export interface RulePart {
|
||||
memo?: string;
|
||||
}
|
||||
|
||||
// Light wrapper for reading records or user attributes.
|
||||
export interface InfoView {
|
||||
get(key: string): CellValue;
|
||||
toJSON(): {[key: string]: any};
|
||||
}
|
||||
|
||||
// As InfoView, but also supporting writing.
|
||||
export interface InfoEditor {
|
||||
get(key: string): CellValue;
|
||||
@@ -38,22 +31,6 @@ export interface InfoEditor {
|
||||
toJSON(): {[key: string]: any};
|
||||
}
|
||||
|
||||
// Represents user info, which may include properties which are themselves RowRecords.
|
||||
export interface UserInfo {
|
||||
Name: string | null;
|
||||
Email: string | null;
|
||||
Access: Role | null;
|
||||
Origin: string | null;
|
||||
LinkKey: Record<string, string | undefined>;
|
||||
UserID: number | null;
|
||||
UserRef: string | null;
|
||||
SessionID: string | null;
|
||||
ShareRef: number | null; // This is a rowId in the _grist_Shares table, if the user
|
||||
// is accessing a document via a share. Otherwise null.
|
||||
[attributes: string]: unknown;
|
||||
toJSON(): {[key: string]: any};
|
||||
}
|
||||
|
||||
export interface UserAttributeRule {
|
||||
origRecord?: RowRecord; // Original record used to create this UserAttributeRule.
|
||||
name: string; // Should be unique among UserAttributeRules.
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
*/
|
||||
import {CellValue, RowRecord} from 'app/common/DocActions';
|
||||
import {ErrorWithCode} from 'app/common/ErrorWithCode';
|
||||
import {InfoView, UserInfo} from 'app/common/GranularAccessClause';
|
||||
import {InfoView} from 'app/common/RecordView';
|
||||
import {UserInfo} from 'app/common/User';
|
||||
import {decodeObject} from 'app/plugin/objtypes';
|
||||
import constant = require('lodash/constant');
|
||||
|
||||
@@ -31,11 +32,6 @@ export interface PredicateFormulaInput {
|
||||
choice?: string|RowRecord|InfoView;
|
||||
}
|
||||
|
||||
export class EmptyRecordView implements InfoView {
|
||||
public get(_colId: string): CellValue { return null; }
|
||||
public toJSON() { return {}; }
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of compiling ParsedPredicateFormula.
|
||||
*/
|
||||
@@ -102,7 +98,7 @@ export function compilePredicateFormula(
|
||||
break;
|
||||
}
|
||||
case 'dropdown-condition': {
|
||||
validNames = ['rec', 'choice'];
|
||||
validNames = ['rec', 'choice', 'user'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
43
app/common/RecordView.ts
Normal file
43
app/common/RecordView.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {CellValue, TableDataAction} from 'app/common/DocActions';
|
||||
|
||||
/** Light wrapper for reading records or user attributes. */
|
||||
export interface InfoView {
|
||||
get(key: string): CellValue;
|
||||
toJSON(): {[key: string]: any};
|
||||
}
|
||||
|
||||
/**
|
||||
* A row-like view of TableDataAction, which is columnar in nature.
|
||||
*
|
||||
* If index value is undefined, acts as an EmptyRecordRow.
|
||||
*/
|
||||
export class RecordView implements InfoView {
|
||||
public constructor(public data: TableDataAction, public index: number|undefined) {
|
||||
}
|
||||
|
||||
public get(colId: string): CellValue {
|
||||
if (this.index === undefined) { return null; }
|
||||
if (colId === 'id') {
|
||||
return this.data[2][this.index];
|
||||
}
|
||||
return this.data[3][colId]?.[this.index];
|
||||
}
|
||||
|
||||
public has(colId: string) {
|
||||
return colId === 'id' || colId in this.data[3];
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
if (this.index === undefined) { return {}; }
|
||||
const results: {[key: string]: any} = {id: this.index};
|
||||
for (const key of Object.keys(this.data[3])) {
|
||||
results[key] = this.data[3][key]?.[this.index];
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
export class EmptyRecordView implements InfoView {
|
||||
public get(_colId: string): CellValue { return null; }
|
||||
public toJSON() { return {}; }
|
||||
}
|
||||
89
app/common/User.ts
Normal file
89
app/common/User.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import {getTableId} from 'app/common/DocActions';
|
||||
import {EmptyRecordView, RecordView} from 'app/common/RecordView';
|
||||
import {Role} from 'app/common/roles';
|
||||
|
||||
/**
|
||||
* Information about a user, including any user attributes.
|
||||
*/
|
||||
export interface UserInfo {
|
||||
Name: string | null;
|
||||
Email: string | null;
|
||||
Access: Role | null;
|
||||
Origin: string | null;
|
||||
LinkKey: Record<string, string | undefined>;
|
||||
UserID: number | null;
|
||||
UserRef: string | null;
|
||||
SessionID: string | null;
|
||||
/**
|
||||
* This is a rowId in the _grist_Shares table, if the user is accessing a document
|
||||
* via a share. Otherwise null.
|
||||
*/
|
||||
ShareRef: number | null;
|
||||
[attributes: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for `UserInfo`.
|
||||
*
|
||||
* Contains methods for converting itself to different representations.
|
||||
*/
|
||||
export class User implements UserInfo {
|
||||
public Name: string | null = null;
|
||||
public UserID: number | null = null;
|
||||
public Access: Role | null = null;
|
||||
public Origin: string | null = null;
|
||||
public LinkKey: Record<string, string | undefined> = {};
|
||||
public Email: string | null = null;
|
||||
public SessionID: string | null = null;
|
||||
public UserRef: string | null = null;
|
||||
public ShareRef: number | null = null;
|
||||
[attribute: string]: any;
|
||||
|
||||
constructor(info: Record<string, unknown> = {}) {
|
||||
Object.assign(this, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON representation of this class that excludes full row data,
|
||||
* only keeping user info and table/row ids for any user attributes.
|
||||
*
|
||||
* Used by the sandbox to support `user` variables in formulas (see `user.py`).
|
||||
*/
|
||||
public toJSON() {
|
||||
return this._toObject((value) => {
|
||||
if (value instanceof RecordView) {
|
||||
return [getTableId(value.data), value.get('id')];
|
||||
} else if (value instanceof EmptyRecordView) {
|
||||
return null;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a record representation of this class, with all user attributes
|
||||
* converted from `RecordView` instances to their JSON representations.
|
||||
*
|
||||
* Used by the client to support `user` variables in dropdown conditions.
|
||||
*/
|
||||
public toUserInfo(): UserInfo {
|
||||
return this._toObject((value) => {
|
||||
if (value instanceof RecordView) {
|
||||
return value.toJSON();
|
||||
} else if (value instanceof EmptyRecordView) {
|
||||
return null;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}) as UserInfo;
|
||||
}
|
||||
|
||||
private _toObject(mapValue: (value: unknown) => unknown) {
|
||||
const results: {[key: string]: any} = {};
|
||||
for (const [key, value] of Object.entries(this)) {
|
||||
results[key] = mapValue(value);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user