Skip to content

Commit 10d9ee6

Browse files
- Updated the Obsidian Data View Filter Editor to use security grant for authorized entity types.
1 parent 6b155f3 commit 10d9ee6

19 files changed

Lines changed: 295 additions & 274 deletions

Rock.Blocks/Example/ControlGallery.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ private string GetSecurityGrantToken()
8484
securityGrant.AddRule( new AssetAndFileManagerSecurityGrantRule( Rock.Security.Authorization.DELETE ) );
8585
securityGrant.AddRule( new EmailEditorSecurityGrantRule() );
8686
securityGrant.AddRule( new ConnectionRequestPickerSecurityGrantRule() );
87+
securityGrant.AddRule( new DataViewFilterEditorSecurityGrantRule()
88+
{
89+
// Only allow the gallery's Data View Filter Editor to be used for editing Person data view filters.
90+
EntityTypeGuid = Rock.SystemGuid.EntityType.PERSON.AsGuid()
91+
} );
8792

8893
return securityGrant.ToToken();
8994
}

Rock.Enums/Reporting/FilterGroupJoinType.cs

Lines changed: 0 additions & 35 deletions
This file was deleted.

Rock.Enums/Reporting/FilterGroupTruthType.cs

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,43 @@
11
<template>
22
<GalleryAndResult :importCode="importCode"
33
:exampleCode="exampleCode"
4-
description="Edits a nested DataViewFilter tree using the same filter components that Data Views and Content Channel Views use.">
4+
description="Edits a nested DataViewFilter tree using the same filter components that Data Views use.">
5+
6+
<NotificationBox alertType="info" heading="Note">
7+
This control is for internal use only.
8+
</NotificationBox>
59

610
<DataViewFilterEditor v-model="value"
7-
:entityTypeGuid="entityTypeGuid?.value" />
11+
:entityTypeGuid="EntityType.Person" />
812

913
<template #settings>
10-
<div class="row">
11-
<div class="col-md-3">
12-
<EntityTypePicker label="For Entity Type"
13-
v-model="entityTypeGuid"
14-
enhanceForLongLists
15-
showBlankItem />
16-
</div>
17-
</div>
14+
<p>This gallery displays the Data View Filters for the <code>Person</code> Entity Type.</p>
1815
</template>
1916
</GalleryAndResult>
2017
</template>
2118

2219
<script setup lang="ts">
23-
import { ref, watch } from "vue";
20+
import { ref } from "vue";
2421
import GalleryAndResult from "./common/galleryAndResult.partial.obs";
2522
import { getSfcControlImportPath } from "./common/utils.partial";
2623
import DataViewFilterEditor from "@Obsidian/Controls/dataViewFilterEditor.obs";
27-
import EntityTypePicker from "@Obsidian/Controls/entityTypePicker.obs";
28-
import { createDefaultDataViewFilter } from "@Obsidian/Core/Reporting/dataViewFilterEditor";
24+
import NotificationBox from "@Obsidian/Controls/notificationBox.obs";
25+
import { FilterExpressionType } from "@Obsidian/Enums/Reporting/filterExpressionType";
2926
import { EntityType } from "@Obsidian/SystemGuids/entityType";
27+
import { newGuid } from "@Obsidian/Utility/guid";
3028
import { DataViewFilterBag } from "@Obsidian/ViewModels/Reporting/dataViewFilterBag";
31-
import type { ListItemBag } from "@Obsidian/ViewModels/Utility/listItemBag";
32-
33-
const defaultEntityTypeGuid = EntityType.Person;
3429

35-
const entityTypeGuid = ref<ListItemBag | null>({ text: "Person", value: defaultEntityTypeGuid });
36-
const value = ref<DataViewFilterBag>(createDefaultDataViewFilter());
30+
const value = ref<DataViewFilterBag>({
31+
expressionType: FilterExpressionType.GroupAll,
32+
guid: newGuid(),
33+
childFilters: [
34+
{
35+
guid: newGuid(),
36+
expressionType: FilterExpressionType.Filter
37+
}
38+
]
39+
});
3740

3841
const importCode = getSfcControlImportPath("dataViewFilterEditor");
39-
const exampleCode = `<DataViewFilterEditor v-model="value" :entityTypeGuid="entityTypeGuid" />`;
40-
41-
watch(entityTypeGuid, () => {
42-
value.value = createDefaultDataViewFilter();
43-
});
42+
const exampleCode = `<DataViewFilterEditor v-model="value" :entityTypeGuid="EntityType.Person" />`;
4443
</script>

Rock.JavaScript.Obsidian/Framework/Controls/Internal/DataViewFilter/dataViewFilterNode.obs

Lines changed: 97 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
</template>
7777
</template>
7878

79-
<div class="filter-group-list">
79+
<div v-if="childFilters.length" class="filter-group-list">
8080
<DataViewFilterNode v-for="(child, index) in childFilters"
8181
:key="child.guid"
8282
:modelValue="child"
@@ -87,6 +87,8 @@
8787
@update:modelValue="onUpdateChild(index, $event)"
8888
@delete="removeChild(index)" />
8989
</div>
90+
91+
<p v-else class="text-muted">No filters. Click "Add Filter" or "Add Group" to get started.</p>
9092
</Panel>
9193

9294
<Panel v-else
@@ -214,21 +216,10 @@
214216
import NotificationBox from "@Obsidian/Controls/notificationBox.obs";
215217
import Panel from "@Obsidian/Controls/panel.obs";
216218
import RockButton from "@Obsidian/Controls/rockButton.obs";
217-
import {
218-
cloneFilterNode,
219-
createFilterNode,
220-
createGroupNode,
221-
getGroupJoinType,
222-
getGroupTruthType,
223-
isFilterNode,
224-
setGroupJoinType,
225-
setGroupTruthType
226-
} from "@Obsidian/Core/Reporting/dataViewFilterEditor";
227219
import { FilterExpressionType } from "@Obsidian/Enums/Reporting/filterExpressionType";
228-
import { FilterGroupJoinType } from "@Obsidian/Enums/Reporting/filterGroupJoinType";
229-
import { FilterGroupTruthType } from "@Obsidian/Enums/Reporting/filterGroupTruthType";
230220
import { FilterMode } from "@Obsidian/Enums/Reporting/filterMode";
231221
import type { Guid } from "@Obsidian/Types";
222+
import { useSecurityGrantToken } from "@Obsidian/Utility/block";
232223
import { useHttp } from "@Obsidian/Utility/http";
233224
import { Enumerable } from "@Obsidian/Utility/linq";
234225
import { toNumberOrNull } from "@Obsidian/Utility/numberUtils";
@@ -237,12 +228,28 @@
237228
import { DataViewFilterBag } from "@Obsidian/ViewModels/Reporting/dataViewFilterBag";
238229
import { DataFilterGetComponentResultsBag } from "@Obsidian/ViewModels/Rest/Controls/dataFilterGetComponentResultsBag";
239230
import { DataFilterGetSelectionResultsBag } from "@Obsidian/ViewModels/Rest/Controls/dataFilterGetSelectionResultsBag";
231+
import { DataFilterExecuteComponentRequestOptionsBag } from "@Obsidian/ViewModels/Rest/Controls/dataFilterExecuteComponentRequestOptionsBag";
240232
import type { ListItemBag } from "@Obsidian/ViewModels/Utility/listItemBag";
233+
import { newGuid } from "@Obsidian/Utility/guid";
241234

242235
defineOptions({
243236
name: "DataViewFilterNode"
244237
});
245238

239+
// #region Types
240+
241+
enum FilterGroupJoinType {
242+
All = 0,
243+
Any = 1
244+
}
245+
246+
enum FilterGroupTruthType {
247+
True = 0,
248+
False = 1
249+
}
250+
251+
// #endregion Types
252+
246253
const props = defineProps({
247254
modelValue: {
248255
type: Object as PropType<DataViewFilterBag>,
@@ -272,6 +279,7 @@
272279
}>();
273280

274281
const http = useHttp();
282+
const securityGrantToken = useSecurityGrantToken();
275283
const node = ref<DataViewFilterBag>(cloneFilterNode(props.modelValue));
276284
const isNodeExpanded = ref<boolean>(props.isRoot || props.isExpanded);
277285
const componentDefinition = ref<DataFilterGetComponentResultsBag["componentDefinition"]>();
@@ -346,7 +354,8 @@
346354
const result = await http.post<DataFilterGetComponentResultsBag>("/api/v2/Controls/DataFilterGetComponent", undefined, {
347355
entityTypeGuid: props.entityTypeGuid,
348356
filterTypeGuid: selectedFilterTypeGuidOrEmptyString.value,
349-
selection: node.value.selection
357+
selection: node.value.selection,
358+
securityGrantToken: securityGrantToken.value
350359
});
351360

352361
if (result.isSuccess && result.data) {
@@ -380,7 +389,8 @@
380389
const result = await http.post<DataFilterGetSelectionResultsBag>("/api/v2/Controls/DataFilterGetSelection", undefined, {
381390
entityTypeGuid: props.entityTypeGuid,
382391
filterTypeGuid: selectedFilterTypeGuidOrEmptyString.value,
383-
componentData: componentData.value
392+
componentData: componentData.value,
393+
securityGrantToken: securityGrantToken.value
384394
});
385395

386396
if (result.isSuccess && result.data) {
@@ -405,15 +415,18 @@
405415
}
406416

407417
async function executeComponentRequest(request: Record<string, string>): Promise<Record<string, string> | null> {
408-
if (!selectedFilterTypeGuidOrEmptyString.value) {
418+
if (!selectedFilterTypeGuidOrEmptyString.value || !props.entityTypeGuid) {
409419
return null;
410420
}
411421

412-
const result = await http.post<Record<string, string>>("/api/v2/Controls/DataFilterExecuteComponentRequest", undefined, {
422+
const options: DataFilterExecuteComponentRequestOptionsBag = {
413423
entityTypeGuid: props.entityTypeGuid,
414424
filterTypeGuid: selectedFilterTypeGuidOrEmptyString.value,
415-
request
416-
});
425+
request,
426+
securityGrantToken: securityGrantToken.value
427+
};
428+
429+
const result = await http.post<Record<string, string>>("/api/v2/Controls/DataFilterExecuteComponentRequest", undefined, options);
417430

418431
if (result.isSuccess) {
419432
return result.data ?? null;
@@ -499,6 +512,71 @@
499512
emit("update:modelValue", nextNode);
500513
}
501514

515+
function cloneFilterNode(node: DataViewFilterBag | null | undefined): DataViewFilterBag {
516+
return JSON.parse(JSON.stringify(node ?? createDefaultDataViewFilter()));
517+
}
518+
519+
function createFilterNode(): DataViewFilterBag {
520+
return {
521+
guid: newGuid(),
522+
expressionType: FilterExpressionType.Filter,
523+
filterTypeGuid: null,
524+
selection: null,
525+
componentData: {},
526+
childFilters: []
527+
};
528+
}
529+
530+
function createGroupNode(expressionType: FilterExpressionType = FilterExpressionType.GroupAll): DataViewFilterBag {
531+
return {
532+
guid: newGuid(),
533+
expressionType,
534+
childFilters: [createFilterNode()]
535+
};
536+
}
537+
538+
function createDefaultDataViewFilter(): DataViewFilterBag {
539+
return createGroupNode(FilterExpressionType.GroupAll);
540+
}
541+
542+
function isFilterNode(node: DataViewFilterBag): boolean {
543+
return node.expressionType === FilterExpressionType.Filter;
544+
}
545+
546+
function getGroupJoinType(expressionType: FilterExpressionType): FilterGroupJoinType {
547+
return expressionType === FilterExpressionType.GroupAny || expressionType === FilterExpressionType.GroupAnyFalse
548+
? FilterGroupJoinType.Any
549+
: FilterGroupJoinType.All;
550+
}
551+
552+
function getGroupTruthType(expressionType: FilterExpressionType): FilterGroupTruthType {
553+
return expressionType === FilterExpressionType.GroupAllFalse || expressionType === FilterExpressionType.GroupAnyFalse
554+
? FilterGroupTruthType.False
555+
: FilterGroupTruthType.True;
556+
}
557+
558+
function setGroupJoinType(node: DataViewFilterBag, joinType: FilterGroupJoinType): void {
559+
const isFalse = node.expressionType === FilterExpressionType.GroupAllFalse || node.expressionType === FilterExpressionType.GroupAnyFalse;
560+
561+
if (joinType === FilterGroupJoinType.All) {
562+
node.expressionType = isFalse ? FilterExpressionType.GroupAllFalse : FilterExpressionType.GroupAll;
563+
}
564+
else {
565+
node.expressionType = isFalse ? FilterExpressionType.GroupAnyFalse : FilterExpressionType.GroupAny;
566+
}
567+
}
568+
569+
function setGroupTruthType(node: DataViewFilterBag, truthType: FilterGroupTruthType): void {
570+
const isAll = node.expressionType === FilterExpressionType.GroupAll || node.expressionType === FilterExpressionType.GroupAllFalse;
571+
572+
if (truthType === FilterGroupTruthType.True) {
573+
node.expressionType = isAll ? FilterExpressionType.GroupAll : FilterExpressionType.GroupAny;
574+
}
575+
else {
576+
node.expressionType = isAll ? FilterExpressionType.GroupAllFalse : FilterExpressionType.GroupAnyFalse;
577+
}
578+
}
579+
502580
watch(() => props.modelValue, async () => {
503581
const incomingNode = cloneFilterNode(props.modelValue);
504582

0 commit comments

Comments
 (0)