Merge branch 'release' into feat/4182-form-detect-changes
This commit is contained in:
commit
ff2bcc228a
|
|
@ -61,7 +61,7 @@
|
||||||
"options": "[{'label':'Vegetarian','value':'VEG'},{'label':'Non-Vegetarian','value':'NON_VEG'},{'label':'Vegan','value':'VEGAN'}]",
|
"options": "[{'label':'Vegetarian','value':'VEG'},{'label':'Non-Vegetarian','value':'NON_VEG'},{'label':'Vegan','value':'VEGAN'}]",
|
||||||
"widgetName": "Dropdown1",
|
"widgetName": "Dropdown1",
|
||||||
"defaultOptionValue": "VEG",
|
"defaultOptionValue": "VEG",
|
||||||
"type": "DROP_DOWN_WIDGET",
|
"type": "SELECT_WIDGET",
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"parentColumnSpace": 74,
|
"parentColumnSpace": 74,
|
||||||
"parentRowSpace": 40,
|
"parentRowSpace": 40,
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@
|
||||||
"topRow": 8,
|
"topRow": 8,
|
||||||
"bottomRow": 15,
|
"bottomRow": 15,
|
||||||
"parentRowSpace": 10,
|
"parentRowSpace": 10,
|
||||||
"type": "DROP_DOWN_WIDGET",
|
"type": "SELECT_WIDGET",
|
||||||
"serverSideFiltering": false,
|
"serverSideFiltering": false,
|
||||||
"hideCard": false,
|
"hideCard": false,
|
||||||
"defaultOptionValue": "GREEN",
|
"defaultOptionValue": "GREEN",
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
"parentRowSpace": 38,
|
"parentRowSpace": 38,
|
||||||
"isVisible": true,
|
"isVisible": true,
|
||||||
"label": "Test Dropdown",
|
"label": "Test Dropdown",
|
||||||
"type": "DROP_DOWN_WIDGET",
|
"type": "SELECT_WIDGET",
|
||||||
"dynamicBindingPathList": [],
|
"dynamicBindingPathList": [],
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"selectionType": "",
|
"selectionType": "",
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ describe("API Panel request body", function() {
|
||||||
cy.SelectAction(testdata.getAction);
|
cy.SelectAction(testdata.getAction);
|
||||||
|
|
||||||
cy.contains(apiEditor.bodyTab).click();
|
cy.contains(apiEditor.bodyTab).click();
|
||||||
|
cy.get(`[data-cy=${testdata.apiContentTypeNone}]`).click();
|
||||||
cy.get(testdata.noBodyErrorMessageDiv).should("exist");
|
cy.get(testdata.noBodyErrorMessageDiv).should("exist");
|
||||||
cy.get(testdata.noBodyErrorMessageDiv).contains(
|
cy.get(testdata.noBodyErrorMessageDiv).contains(
|
||||||
testdata.noBodyErrorMessage,
|
testdata.noBodyErrorMessage,
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,11 @@ describe("Check debugger logs state when there are onPageLoad actions", function
|
||||||
cy.CreateAPI("TestApi");
|
cy.CreateAPI("TestApi");
|
||||||
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods);
|
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods);
|
||||||
cy.SaveAndRunAPI();
|
cy.SaveAndRunAPI();
|
||||||
|
|
||||||
cy.get(explorer.addWidget).click();
|
cy.get(explorer.addWidget).click();
|
||||||
|
|
||||||
cy.reload();
|
cy.reload();
|
||||||
// Wait for the debugger icon to be visible
|
// Wait for the debugger icon to be visible
|
||||||
cy.get(".t--debugger").should("be.visible");
|
cy.get(".t--debugger").should("be.visible");
|
||||||
cy.get(debuggerLocators.errorCount).should("not.exist");
|
//cy.get(debuggerLocators.errorCount).should("not.exist");
|
||||||
cy.wait("@postExecute");
|
cy.wait("@postExecute");
|
||||||
cy.contains(debuggerLocators.errorCount, 1);
|
cy.contains(debuggerLocators.errorCount, 1);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,12 @@ describe("Chart Widget Skeleton Loading Functionality", function() {
|
||||||
//Step9:
|
//Step9:
|
||||||
cy.get(".bp3-button-text")
|
cy.get(".bp3-button-text")
|
||||||
.first()
|
.first()
|
||||||
.click();
|
.click({ force: true });
|
||||||
|
|
||||||
//Step10:
|
//Step10:
|
||||||
cy.get(".t--widget-chartwidget div[class*='bp3-skeleton']").should("exist");
|
cy.get(".t--widget-chartwidget div[class*='bp3-skeleton']").should("exist");
|
||||||
|
|
||||||
|
/* This section is flaky hence commenting out
|
||||||
//Step11:
|
//Step11:
|
||||||
cy.reload();
|
cy.reload();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
const dsl = require("../../../../fixtures/emptyDSL.json");
|
const dsl = require("../../../../fixtures/emptyDSL.json");
|
||||||
const explorer = require("../../../../locators/explorerlocators.json");
|
const explorer = require("../../../../locators/explorerlocators.json");
|
||||||
|
const formWidgetsPage = require("../../../../locators/FormWidgets.json");
|
||||||
|
const commonlocators = require("../../../../locators/commonlocators.json");
|
||||||
|
const publish = require("../../../../locators/publishWidgetspage.json");
|
||||||
|
|
||||||
describe("Dropdown Widget Functionality", function() {
|
describe("Dropdown Widget Functionality", function() {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.addDsl(dsl);
|
cy.addDsl(dsl);
|
||||||
});
|
});
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.wait(7000);
|
||||||
|
});
|
||||||
|
|
||||||
it("Add new dropdown widget", () => {
|
it("Add new dropdown widget", () => {
|
||||||
cy.get(explorer.addWidget).click();
|
cy.get(explorer.addWidget).click();
|
||||||
|
|
@ -36,7 +42,7 @@ describe("Dropdown Widget Functionality", function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should check that more thatn empty value is not allowed in options", () => {
|
it("should check that more than one empty value is not allowed in options", () => {
|
||||||
cy.openPropertyPane("selectwidget");
|
cy.openPropertyPane("selectwidget");
|
||||||
cy.updateCodeInput(
|
cy.updateCodeInput(
|
||||||
".t--property-control-options",
|
".t--property-control-options",
|
||||||
|
|
@ -59,4 +65,61 @@ describe("Dropdown Widget Functionality", function() {
|
||||||
"exist",
|
"exist",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.skip("should check that Objects can be added to Select Widget default value", () => {
|
||||||
|
cy.openPropertyPane("selectwidget");
|
||||||
|
cy.updateCodeInput(
|
||||||
|
".t--property-control-options",
|
||||||
|
`[
|
||||||
|
{
|
||||||
|
"label": "Blue",
|
||||||
|
"value": "BLUE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Green",
|
||||||
|
"value": "GREEN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Red",
|
||||||
|
"value": "RED"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
);
|
||||||
|
cy.updateCodeInput(
|
||||||
|
".t--property-control-defaultvalue",
|
||||||
|
`
|
||||||
|
{
|
||||||
|
"label": "Green",
|
||||||
|
"value": "GREEN"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
cy.get(".t--property-control-options .t--codemirror-has-error").should(
|
||||||
|
"not.exist",
|
||||||
|
);
|
||||||
|
cy.get(".t--property-control-defaultvalue .t--codemirror-has-error").should(
|
||||||
|
"not.exist",
|
||||||
|
);
|
||||||
|
cy.get(formWidgetsPage.dropdownDefaultButton).should("contain", "Green");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Dropdown Functionality To Check disabled Widget", function() {
|
||||||
|
cy.openPropertyPane("selectwidget");
|
||||||
|
// Disable the visible JS
|
||||||
|
cy.togglebarDisable(commonlocators.visibleCheckbox);
|
||||||
|
cy.PublishtheApp();
|
||||||
|
// Verify the disabled visible JS
|
||||||
|
cy.get(publish.selectwidget + " " + "input").should("not.exist");
|
||||||
|
cy.goToEditFromPublish();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Dropdown Functionality To UnCheck disabled Widget", function() {
|
||||||
|
cy.openPropertyPane("selectwidget");
|
||||||
|
// Check the visible JS
|
||||||
|
cy.togglebar(commonlocators.visibleCheckbox);
|
||||||
|
cy.PublishtheApp();
|
||||||
|
// Verify the checked visible JS
|
||||||
|
cy.get(publish.selectwidget).should("exist");
|
||||||
|
cy.goToEditFromPublish();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
const dsl = require("../../../../fixtures/emptyDSL.json");
|
const dsl = require("../../../../fixtures/emptyDSL.json");
|
||||||
const explorer = require("../../../../locators/explorerlocators.json");
|
const explorer = require("../../../../locators/explorerlocators.json");
|
||||||
|
const formWidgetsPage = require("../../../../locators/FormWidgets.json");
|
||||||
|
|
||||||
describe("MultiSelect Widget Functionality", function() {
|
describe("MultiSelect Widget Functionality", function() {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.addDsl(dsl);
|
cy.addDsl(dsl);
|
||||||
});
|
});
|
||||||
|
beforeEach(() => {
|
||||||
it("Add new dropdown widget", () => {
|
cy.wait(7000);
|
||||||
|
});
|
||||||
|
it("Add new multiselect widget", () => {
|
||||||
cy.get(explorer.addWidget).click();
|
cy.get(explorer.addWidget).click();
|
||||||
cy.dragAndDropToCanvas("multiselectwidgetv2", { x: 300, y: 300 });
|
cy.dragAndDropToCanvas("multiselectwidgetv2", { x: 300, y: 300 });
|
||||||
cy.get(".t--widget-multiselectwidgetv2").should("exist");
|
cy.get(".t--widget-multiselectwidgetv2").should("exist");
|
||||||
|
|
@ -36,7 +39,7 @@ describe("MultiSelect Widget Functionality", function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should check that more thatn empty value is not allowed in options", () => {
|
it("should check that more that one empty value is not allowed in options", () => {
|
||||||
cy.openPropertyPane("multiselectwidgetv2");
|
cy.openPropertyPane("multiselectwidgetv2");
|
||||||
cy.updateCodeInput(
|
cy.updateCodeInput(
|
||||||
".t--property-control-options",
|
".t--property-control-options",
|
||||||
|
|
@ -59,4 +62,44 @@ describe("MultiSelect Widget Functionality", function() {
|
||||||
"exist",
|
"exist",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it("should check that Objects can be added to multiselect Widget default value", () => {
|
||||||
|
cy.openPropertyPane("multiselectwidgetv2");
|
||||||
|
cy.updateCodeInput(
|
||||||
|
".t--property-control-options",
|
||||||
|
`[
|
||||||
|
{
|
||||||
|
"label": "Blue",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Green",
|
||||||
|
"value": "GREEN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Red",
|
||||||
|
"value": "RED"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
);
|
||||||
|
cy.updateCodeInput(
|
||||||
|
".t--property-control-defaultvalue",
|
||||||
|
`[
|
||||||
|
{
|
||||||
|
"label": "Green",
|
||||||
|
"value": "GREEN"
|
||||||
|
}
|
||||||
|
]`,
|
||||||
|
);
|
||||||
|
cy.get(".t--property-control-options .t--codemirror-has-error").should(
|
||||||
|
"not.exist",
|
||||||
|
);
|
||||||
|
cy.get(".t--property-control-defaultvalue .t--codemirror-has-error").should(
|
||||||
|
"not.exist",
|
||||||
|
);
|
||||||
|
cy.wait(100);
|
||||||
|
cy.get(formWidgetsPage.multiselectwidgetv2)
|
||||||
|
.find(".rc-select-selection-item-content")
|
||||||
|
.first()
|
||||||
|
.should("have.text", "Green");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
const dsl = require("../../../../fixtures/previewMode.json");
|
const dsl = require("../../../../fixtures/previewMode.json");
|
||||||
|
const commonlocators = require("../../../../locators/commonlocators.json");
|
||||||
|
const publishPage = require("../../../../locators/publishWidgetspage.json");
|
||||||
|
|
||||||
describe("Preview mode functionality", function() {
|
describe("Preview mode functionality", function() {
|
||||||
before(() => {
|
before(() => {
|
||||||
|
|
@ -26,4 +28,25 @@ describe("Preview mode functionality", function() {
|
||||||
`${selector}:first-of-type .t--widget-propertypane-toggle > .t--widget-name`,
|
`${selector}:first-of-type .t--widget-propertypane-toggle > .t--widget-name`,
|
||||||
).should("not.exist");
|
).should("not.exist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("check invisible widget should not show in proview mode and should show in edit mode", function() {
|
||||||
|
cy.get(".t--switch-comment-mode-off").click();
|
||||||
|
cy.openPropertyPane("buttonwidget");
|
||||||
|
cy.UncheckWidgetProperties(commonlocators.visibleCheckbox);
|
||||||
|
|
||||||
|
// button should not show in preview mode
|
||||||
|
cy.get(".t--switch-preview-mode-toggle").click();
|
||||||
|
cy.get(`${publishPage.buttonWidget} button`).should("not.exist");
|
||||||
|
|
||||||
|
// Text widget should show
|
||||||
|
cy.get(`${publishPage.textWidget} .bp3-ui-text`).should("exist");
|
||||||
|
|
||||||
|
// button should show in edit mode
|
||||||
|
cy.get(".t--switch-comment-mode-off").click();
|
||||||
|
cy.get(`${publishPage.buttonWidget} button`).should("exist");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// put your clean up code if any
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"checkboxWidget": ".t--draggable-checkboxwidget",
|
"checkboxWidget": ".t--draggable-checkboxwidget",
|
||||||
"selectwidget": ".t--draggable-selectwidget",
|
"selectwidget": ".t--draggable-selectwidget",
|
||||||
"dropdownWidget": ".t--draggable-dropdownwidget",
|
"dropdownWidget": ".t--draggable-selectwidget",
|
||||||
"menuButtonWidget": ".t--draggable-menubuttonwidget",
|
"menuButtonWidget": ".t--draggable-menubuttonwidget",
|
||||||
"multiselectwidgetv2": ".t--draggable-multiselectwidgetv2",
|
"multiselectwidgetv2": ".t--draggable-multiselectwidgetv2",
|
||||||
"multiselecttreeWidget": ".t--draggable-multiselecttreewidget",
|
"multiselecttreeWidget": ".t--draggable-multiselecttreewidget",
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@
|
||||||
"widgetName":"Dropdown1",
|
"widgetName":"Dropdown1",
|
||||||
"defaultOptionValue":"VEG",
|
"defaultOptionValue":"VEG",
|
||||||
"version":1,
|
"version":1,
|
||||||
"type":"DROP_DOWN_WIDGET",
|
"type":"SELECT_WIDGET",
|
||||||
"isLoading":false,
|
"isLoading":false,
|
||||||
"parentColumnSpace":60.131249999999994,
|
"parentColumnSpace":60.131249999999994,
|
||||||
"parentRowSpace":40,
|
"parentRowSpace":40,
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ const TextInput = forwardRef(
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState(props.defaultValue);
|
const [inputValue, setInputValue] = useState(props.defaultValue);
|
||||||
|
|
||||||
const { trimValue = true } = props;
|
const { trimValue = false } = props;
|
||||||
|
|
||||||
const setRightSideRef = useCallback((ref: HTMLDivElement) => {
|
const setRightSideRef = useCallback((ref: HTMLDivElement) => {
|
||||||
if (ref) {
|
if (ref) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { previewModeSelector } from "selectors/editorSelectors";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
isVisible?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* render only visible components in preview mode
|
||||||
|
*/
|
||||||
|
function PreviewModeComponent({
|
||||||
|
children,
|
||||||
|
isVisible,
|
||||||
|
}: Props): React.ReactElement {
|
||||||
|
const isPreviewMode = useSelector(previewModeSelector);
|
||||||
|
if (!isPreviewMode || isVisible) return children as React.ReactElement;
|
||||||
|
else return <div />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PreviewModeComponent;
|
||||||
|
|
@ -814,7 +814,7 @@ class DatasourceRestAPIEditor extends React.Component<Props> {
|
||||||
value: "HEADER",
|
value: "HEADER",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"Send client credentials with",
|
"Send client credentials with (on refresh token):",
|
||||||
"",
|
"",
|
||||||
false,
|
false,
|
||||||
"",
|
"",
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ import {
|
||||||
} from "utils/ApiPaneUtils";
|
} from "utils/ApiPaneUtils";
|
||||||
import { updateReplayEntity } from "actions/pageActions";
|
import { updateReplayEntity } from "actions/pageActions";
|
||||||
import { ENTITY_TYPE } from "entities/AppsmithConsole";
|
import { ENTITY_TYPE } from "entities/AppsmithConsole";
|
||||||
|
import { getDisplayFormat } from "selectors/apiPaneSelectors";
|
||||||
|
|
||||||
function* syncApiParamsSaga(
|
function* syncApiParamsSaga(
|
||||||
actionPayload: ReduxActionWithMeta<string, { field: string }>,
|
actionPayload: ReduxActionWithMeta<string, { field: string }>,
|
||||||
|
|
@ -138,7 +139,8 @@ function* handleUpdateBodyContentType(
|
||||||
) {
|
) {
|
||||||
const { apiId, title } = action.payload;
|
const { apiId, title } = action.payload;
|
||||||
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
|
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
|
||||||
// this is a previous value gotten before the updated content type has been set
|
|
||||||
|
// this is the previous value gotten before the new content type has been set
|
||||||
const previousContentType =
|
const previousContentType =
|
||||||
values.actionConfiguration?.formData?.apiContentType;
|
values.actionConfiguration?.formData?.apiContentType;
|
||||||
|
|
||||||
|
|
@ -150,31 +152,37 @@ function* handleUpdateBodyContentType(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is the update for the new api contentType
|
// this is the update for the new apicontentType
|
||||||
// update the api content type so it can be persisted.
|
// Quick Context: APiContentype is the field that represents the content type the user wants while in RAW mode.
|
||||||
|
// users should be able to set the content type to whatever they want.
|
||||||
let formData = { ...values.actionConfiguration.formData };
|
let formData = { ...values.actionConfiguration.formData };
|
||||||
if (formData === undefined) formData = {};
|
if (formData === undefined) formData = {};
|
||||||
formData["apiContentType"] = title;
|
formData["apiContentType"] =
|
||||||
|
title === POST_BODY_FORMAT_OPTIONS.RAW ||
|
||||||
|
title === POST_BODY_FORMAT_OPTIONS.NONE
|
||||||
|
? previousContentType
|
||||||
|
: title;
|
||||||
|
|
||||||
yield put(
|
yield put(
|
||||||
change(API_EDITOR_FORM_NAME, "actionConfiguration.formData", formData),
|
change(API_EDITOR_FORM_NAME, "actionConfiguration.formData", formData),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (displayFormatValue === POST_BODY_FORMAT_OPTIONS.RAW) {
|
// Quick Context: The extra formadata action is responsible for updating the current multi switch mode you see on api editor body tab
|
||||||
// update the content type header if raw has been selected
|
// whenever a user selects a new content type through the tab e.g application/json, this action is dispatched to update that value, which is then read in the PostDataBody file
|
||||||
yield put({
|
// to show the appropriate content type section.
|
||||||
type: ReduxActionTypes.SET_EXTRA_FORMDATA,
|
|
||||||
payload: {
|
yield put({
|
||||||
id: apiId,
|
type: ReduxActionTypes.SET_EXTRA_FORMDATA,
|
||||||
values: {
|
payload: {
|
||||||
displayFormat: {
|
id: apiId,
|
||||||
label: displayFormatValue,
|
values: {
|
||||||
value: displayFormatValue,
|
displayFormat: {
|
||||||
},
|
label: title,
|
||||||
|
value: title,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
}
|
});
|
||||||
|
|
||||||
const headers = cloneDeep(values.actionConfiguration.headers);
|
const headers = cloneDeep(values.actionConfiguration.headers);
|
||||||
|
|
||||||
|
|
@ -186,25 +194,19 @@ function* handleUpdateBodyContentType(
|
||||||
);
|
);
|
||||||
const indexToUpdate = getIndextoUpdate(headers, contentTypeHeaderIndex);
|
const indexToUpdate = getIndextoUpdate(headers, contentTypeHeaderIndex);
|
||||||
|
|
||||||
// If the user has selected "None" as the body type & there was a content-type
|
// If the user has selected "None" or "Raw" as the body type & there was a content-type
|
||||||
// header present in the API configuration, keep the previous content type header
|
// header present in the API configuration, keep the previous content type header
|
||||||
// but if the user has selected "raw", set the content header to text/plain
|
// this is done to ensure user input isn't cleared off if they switch to raw or none mode.
|
||||||
|
// however if the user types in a new value, we use the updated value (formValueChangeSaga - line 426).
|
||||||
if (
|
if (
|
||||||
displayFormatValue === POST_BODY_FORMAT_OPTIONS.NONE &&
|
(displayFormatValue === POST_BODY_FORMAT_OPTIONS.NONE ||
|
||||||
|
displayFormatValue === POST_BODY_FORMAT_OPTIONS.RAW) &&
|
||||||
indexToUpdate !== -1
|
indexToUpdate !== -1
|
||||||
) {
|
) {
|
||||||
headers[indexToUpdate] = {
|
headers[indexToUpdate] = {
|
||||||
key: previousContentType ? CONTENT_TYPE_HEADER_KEY : "",
|
key: previousContentType ? CONTENT_TYPE_HEADER_KEY : "",
|
||||||
value: previousContentType ? previousContentType : "",
|
value: previousContentType ? previousContentType : "",
|
||||||
};
|
};
|
||||||
} else if (
|
|
||||||
displayFormatValue === POST_BODY_FORMAT_OPTIONS.RAW &&
|
|
||||||
indexToUpdate !== -1
|
|
||||||
) {
|
|
||||||
headers[indexToUpdate] = {
|
|
||||||
key: CONTENT_TYPE_HEADER_KEY,
|
|
||||||
value: POST_BODY_FORMAT_OPTIONS.RAW,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
headers[indexToUpdate] = {
|
headers[indexToUpdate] = {
|
||||||
key: CONTENT_TYPE_HEADER_KEY,
|
key: CONTENT_TYPE_HEADER_KEY,
|
||||||
|
|
@ -212,6 +214,7 @@ function* handleUpdateBodyContentType(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the new header values.
|
||||||
yield put(
|
yield put(
|
||||||
change(API_EDITOR_FORM_NAME, "actionConfiguration.headers", headers),
|
change(API_EDITOR_FORM_NAME, "actionConfiguration.headers", headers),
|
||||||
);
|
);
|
||||||
|
|
@ -235,18 +238,19 @@ function* handleUpdateBodyContentType(
|
||||||
}
|
}
|
||||||
|
|
||||||
function* initializeExtraFormDataSaga() {
|
function* initializeExtraFormDataSaga() {
|
||||||
const state = yield select();
|
|
||||||
const { extraformData } = state.ui.apiPane;
|
|
||||||
const formData = yield select(getFormData, API_EDITOR_FORM_NAME);
|
const formData = yield select(getFormData, API_EDITOR_FORM_NAME);
|
||||||
const { values } = formData;
|
const { values } = formData;
|
||||||
// const headers = get(values, "actionConfiguration.headers");
|
|
||||||
const apiContentType = get(
|
|
||||||
values,
|
|
||||||
"actionConfiguration.formData.apiContentType",
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!extraformData[values.id]) {
|
// when initializing, check if theres a display format present, if not use Json display format as default.
|
||||||
yield call(setHeaderFormat, values.id, apiContentType);
|
const extraFormData = yield select(getDisplayFormat, values.id);
|
||||||
|
|
||||||
|
// as a fail safe, if no display format is present, use Raw mode
|
||||||
|
const rawApiContentType = extraFormData?.displayFormat?.value
|
||||||
|
? extraFormData?.displayFormat?.value
|
||||||
|
: POST_BODY_FORMAT_OPTIONS.RAW;
|
||||||
|
|
||||||
|
if (!extraFormData) {
|
||||||
|
yield call(setHeaderFormat, values.id, rawApiContentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,6 +300,7 @@ function* changeApiSaga(
|
||||||
function* setHeaderFormat(apiId: string, apiContentType?: string) {
|
function* setHeaderFormat(apiId: string, apiContentType?: string) {
|
||||||
// use the current apiContentType to set appropriate Headers for action
|
// use the current apiContentType to set appropriate Headers for action
|
||||||
let displayFormat;
|
let displayFormat;
|
||||||
|
|
||||||
if (apiContentType) {
|
if (apiContentType) {
|
||||||
if (apiContentType === POST_BODY_FORMAT_OPTIONS.NONE) {
|
if (apiContentType === POST_BODY_FORMAT_OPTIONS.NONE) {
|
||||||
displayFormat = {
|
displayFormat = {
|
||||||
|
|
@ -336,8 +341,9 @@ export function* updateFormFields(
|
||||||
const value = actionPayload.payload;
|
const value = actionPayload.payload;
|
||||||
log.debug("updateFormFields: " + JSON.stringify(value));
|
log.debug("updateFormFields: " + JSON.stringify(value));
|
||||||
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
|
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
|
||||||
let apiContentType = values.actionConfiguration.formData.apiContentType;
|
|
||||||
|
|
||||||
|
// get current content type of the action
|
||||||
|
let apiContentType = values.actionConfiguration.formData.apiContentType;
|
||||||
if (field === "actionConfiguration.httpMethod") {
|
if (field === "actionConfiguration.httpMethod") {
|
||||||
const { actionConfiguration } = values;
|
const { actionConfiguration } = values;
|
||||||
if (!actionConfiguration.headers) return;
|
if (!actionConfiguration.headers) return;
|
||||||
|
|
@ -370,14 +376,7 @@ export function* updateFormFields(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// change apiContentType when user changes api Http Method
|
|
||||||
yield put(
|
|
||||||
change(
|
|
||||||
API_EDITOR_FORM_NAME,
|
|
||||||
"actionConfiguration.formData.apiContentType",
|
|
||||||
apiContentType,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
yield put(
|
yield put(
|
||||||
change(
|
change(
|
||||||
API_EDITOR_FORM_NAME,
|
API_EDITOR_FORM_NAME,
|
||||||
|
|
@ -385,9 +384,6 @@ export function* updateFormFields(
|
||||||
actionConfigurationHeaders,
|
actionConfigurationHeaders,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (field.includes("actionConfiguration.headers")) {
|
|
||||||
const apiId = get(values, "id");
|
|
||||||
yield call(setHeaderFormat, apiId, apiContentType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,29 +420,21 @@ function* formValueChangeSaga(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// when user types a content type value, update actionConfiguration.formData.apiContent type as well.
|
// when user types a content type value, update actionConfiguration.formData.apiContent type as well.
|
||||||
|
// we don't do this initally because we want to specifically catch user editing the content-type value
|
||||||
if (
|
if (
|
||||||
field === `actionConfiguration.headers[${contentTypeHeaderIndex}].value`
|
field === `actionConfiguration.headers[${contentTypeHeaderIndex}].value`
|
||||||
) {
|
) {
|
||||||
if (
|
yield put(
|
||||||
// if the value is not a registered content type, make the default apiContentType raw but don't change header
|
change(
|
||||||
Object.values(POST_BODY_FORMAT_OPTIONS).includes(actionPayload.payload)
|
API_EDITOR_FORM_NAME,
|
||||||
) {
|
"actionConfiguration.formData.apiContentType",
|
||||||
yield put(
|
actionPayload.payload,
|
||||||
change(
|
),
|
||||||
API_EDITOR_FORM_NAME,
|
);
|
||||||
"actionConfiguration.formData.apiContentType",
|
const apiId = get(values, "id");
|
||||||
actionPayload.payload,
|
// when the user specifically sets a new content type value, we check if the input value is a supported post body type and switch to it
|
||||||
),
|
// if it does not we set the default to Raw mode.
|
||||||
);
|
yield call(setHeaderFormat, apiId, actionPayload.payload);
|
||||||
} else {
|
|
||||||
yield put(
|
|
||||||
change(
|
|
||||||
API_EDITOR_FORM_NAME,
|
|
||||||
"actionConfiguration.formData.apiContentType",
|
|
||||||
POST_BODY_FORMAT_OPTIONS.RAW,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
yield all([
|
yield all([
|
||||||
|
|
|
||||||
11
app/client/src/selectors/apiPaneSelectors.ts
Normal file
11
app/client/src/selectors/apiPaneSelectors.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { AppState } from "reducers";
|
||||||
|
|
||||||
|
type GetFormData = (
|
||||||
|
state: AppState,
|
||||||
|
apiId: string,
|
||||||
|
) => { label: string; value: string };
|
||||||
|
|
||||||
|
export const getDisplayFormat: GetFormData = (state, apiId) => {
|
||||||
|
const displayFormat = state.ui.apiPane.extraformData[apiId];
|
||||||
|
return displayFormat;
|
||||||
|
};
|
||||||
|
|
@ -112,7 +112,7 @@ const useHorizontalResize = (
|
||||||
unFocus(document, window);
|
unFocus(document, window);
|
||||||
|
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
const width = ref.current.getBoundingClientRect().width;
|
const width = ref.current.clientWidth;
|
||||||
const current = event.touches[0].clientX;
|
const current = event.touches[0].clientX;
|
||||||
const positionDelta = position - current;
|
const positionDelta = position - current;
|
||||||
const widthDelta = inverse ? -positionDelta : positionDelta;
|
const widthDelta = inverse ? -positionDelta : positionDelta;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import OverlayCommentsWrapper from "comments/inlineComments/OverlayCommentsWrapp
|
||||||
import PreventInteractionsOverlay from "components/editorComponents/PreventInteractionsOverlay";
|
import PreventInteractionsOverlay from "components/editorComponents/PreventInteractionsOverlay";
|
||||||
import AppsmithConsole from "utils/AppsmithConsole";
|
import AppsmithConsole from "utils/AppsmithConsole";
|
||||||
import { ENTITY_TYPE } from "entities/AppsmithConsole";
|
import { ENTITY_TYPE } from "entities/AppsmithConsole";
|
||||||
|
import PreviewModeComponent from "components/editorComponents/PreviewModeComponent";
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* BaseWidget
|
* BaseWidget
|
||||||
|
|
@ -313,12 +314,20 @@ abstract class BaseWidget<
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPreviewModeWidget(content: ReactNode): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<PreviewModeComponent isVisible={this.props.isVisible}>
|
||||||
|
{content}
|
||||||
|
</PreviewModeComponent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private getWidgetView(): ReactNode {
|
private getWidgetView(): ReactNode {
|
||||||
let content: ReactNode;
|
let content: ReactNode;
|
||||||
|
|
||||||
switch (this.props.renderMode) {
|
switch (this.props.renderMode) {
|
||||||
case RenderModes.CANVAS:
|
case RenderModes.CANVAS:
|
||||||
content = this.getCanvasView();
|
content = this.getCanvasView();
|
||||||
|
content = this.addPreviewModeWidget(content);
|
||||||
content = this.addPreventInteractionOverlay(content);
|
content = this.addPreventInteractionOverlay(content);
|
||||||
content = this.addOverlayComments(content);
|
content = this.addOverlayComments(content);
|
||||||
if (!this.props.detachFromLayout) {
|
if (!this.props.detachFromLayout) {
|
||||||
|
|
|
||||||
|
|
@ -207,9 +207,7 @@ class InputWidget extends BaseInputWidget<InputWidgetProps, WidgetState> {
|
||||||
isTriggerProperty: false,
|
isTriggerProperty: false,
|
||||||
validation: {
|
validation: {
|
||||||
type: ValidationTypes.NUMBER,
|
type: ValidationTypes.NUMBER,
|
||||||
params: {
|
params: { min: 1, natural: true },
|
||||||
min: 1,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
hidden: (props: InputWidgetProps) => {
|
hidden: (props: InputWidgetProps) => {
|
||||||
return props.inputType !== InputTypes.TEXT;
|
return props.inputType !== InputTypes.TEXT;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import Icon from "components/ads/Icon";
|
||||||
import { Button, Classes, InputGroup } from "@blueprintjs/core";
|
import { Button, Classes, InputGroup } from "@blueprintjs/core";
|
||||||
import { WidgetContainerDiff } from "widgets/WidgetUtils";
|
import { WidgetContainerDiff } from "widgets/WidgetUtils";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
import _ from "lodash";
|
import { uniqBy } from "lodash";
|
||||||
|
|
||||||
const menuItemSelectedIcon = (props: { isSelected: boolean }) => {
|
const menuItemSelectedIcon = (props: { isSelected: boolean }) => {
|
||||||
return <MenuItemCheckBox checked={props.isSelected} />;
|
return <MenuItemCheckBox checked={props.isSelected} />;
|
||||||
|
|
@ -121,13 +121,12 @@ function MultiSelectComponent({
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// get unique selected values amongst SelectedAllValue and Value
|
// get unique selected values amongst SelectedAllValue and Value
|
||||||
const allSelectedOptions = _.uniqBy(
|
const allSelectedOptions = uniqBy([...allOption, ...value], "value").map(
|
||||||
[...allOption, ...value],
|
(val) => ({
|
||||||
"value",
|
...val,
|
||||||
).map((val) => ({
|
key: val.value,
|
||||||
...val,
|
}),
|
||||||
key: val.value,
|
);
|
||||||
}));
|
|
||||||
onChange(allSelectedOptions);
|
onChange(allSelectedOptions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ export const CONFIG = {
|
||||||
name: "MultiSelect",
|
name: "MultiSelect",
|
||||||
iconSVG: IconSVG,
|
iconSVG: IconSVG,
|
||||||
needsMeta: true,
|
needsMeta: true,
|
||||||
isFilterable: true,
|
|
||||||
defaults: {
|
defaults: {
|
||||||
rows: 7,
|
rows: 7,
|
||||||
columns: 20,
|
columns: 20,
|
||||||
|
|
@ -18,8 +17,9 @@ export const CONFIG = {
|
||||||
{ label: "Red", value: "RED" },
|
{ label: "Red", value: "RED" },
|
||||||
],
|
],
|
||||||
widgetName: "MultiSelect",
|
widgetName: "MultiSelect",
|
||||||
|
isFilterable: true,
|
||||||
serverSideFiltering: false,
|
serverSideFiltering: false,
|
||||||
defaultOptionValue: [{ label: "Green", value: "GREEN" }],
|
defaultOptionValue: ["GREEN", "RED"],
|
||||||
version: 1,
|
version: 1,
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
|
|
|
||||||
249
app/client/src/widgets/MultiSelectWidgetV2/widget/index.test.tsx
Normal file
249
app/client/src/widgets/MultiSelectWidgetV2/widget/index.test.tsx
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
import _ from "lodash";
|
||||||
|
import { defaultOptionValueValidation, MultiSelectWidgetProps } from ".";
|
||||||
|
|
||||||
|
describe("defaultOptionValueValidation - ", () => {
|
||||||
|
it("should get tested with empty string", () => {
|
||||||
|
const input = "";
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: [],
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with array of strings|number", () => {
|
||||||
|
const input = ["green", "red"];
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: input,
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with array json string", () => {
|
||||||
|
const input = `["green", "red"]`;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: ["green", "red"],
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with array of object json string", () => {
|
||||||
|
const input = `[
|
||||||
|
{
|
||||||
|
"label": "green",
|
||||||
|
"value": "green"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "red",
|
||||||
|
"value": "red"
|
||||||
|
}
|
||||||
|
]`;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: [
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
value: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "red",
|
||||||
|
value: "red",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with comma seperated strings", () => {
|
||||||
|
const input = "green, red";
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: ["green", "red"],
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with simple string", () => {
|
||||||
|
const input = "green";
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: ["green"],
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with simple string", () => {
|
||||||
|
const input = `{"green"`;
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: [`{"green"`],
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with array of label, value", () => {
|
||||||
|
const input = [
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
value: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "red",
|
||||||
|
value: "red",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: [
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
value: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "red",
|
||||||
|
value: "red",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with array of invalid values", () => {
|
||||||
|
const testValues = [
|
||||||
|
[
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: [
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: [
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: [
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: [
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[undefined],
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: [
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[true],
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: [
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
["green", "green"],
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: ["values must be unique. Duplicate values found"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
value: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
value: "green",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: ["path:value must be unique. Duplicate values found"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: [],
|
||||||
|
messages: [
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
testValues.forEach(([input, expected]) => {
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as MultiSelectWidgetProps, _),
|
||||||
|
).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -2,8 +2,11 @@ import React from "react";
|
||||||
import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget";
|
import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget";
|
||||||
import { WidgetType } from "constants/WidgetConstants";
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
||||||
import { isArray } from "lodash";
|
import { isArray, isString, isNumber } from "lodash";
|
||||||
import { ValidationTypes } from "constants/WidgetValidation";
|
import {
|
||||||
|
ValidationResponse,
|
||||||
|
ValidationTypes,
|
||||||
|
} from "constants/WidgetValidation";
|
||||||
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
||||||
import MultiSelectComponent from "../component";
|
import MultiSelectComponent from "../component";
|
||||||
import {
|
import {
|
||||||
|
|
@ -12,6 +15,114 @@ import {
|
||||||
} from "rc-select/lib/interface/generator";
|
} from "rc-select/lib/interface/generator";
|
||||||
import { Layers } from "constants/Layers";
|
import { Layers } from "constants/Layers";
|
||||||
import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
|
import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
|
||||||
|
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
|
||||||
|
|
||||||
|
export function defaultOptionValueValidation(
|
||||||
|
value: unknown,
|
||||||
|
props: MultiSelectWidgetProps,
|
||||||
|
_: any,
|
||||||
|
): ValidationResponse {
|
||||||
|
let isValid;
|
||||||
|
let parsed;
|
||||||
|
let message = "";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to check if the object has `label` and `value`
|
||||||
|
*/
|
||||||
|
const hasLabelValue = (obj: any) => {
|
||||||
|
return (
|
||||||
|
_.isPlainObject(obj) &&
|
||||||
|
obj.hasOwnProperty("label") &&
|
||||||
|
obj.hasOwnProperty("value") &&
|
||||||
|
_.isString(obj.label) &&
|
||||||
|
(_.isString(obj.value) || _.isFinite(obj.value))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to check for duplicate values in array
|
||||||
|
*/
|
||||||
|
const hasUniqueValues = (arr: Array<string>) => {
|
||||||
|
const uniqueValues = new Set(arr);
|
||||||
|
|
||||||
|
return uniqueValues.size === arr.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When value is "['green', 'red']", "[{label: 'green', value: 'green'}]" and "green, red"
|
||||||
|
*/
|
||||||
|
if (_.isString(value) && (value as string).trim() !== "") {
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* when value is "['green', 'red']", "[{label: 'green', value: 'green'}]"
|
||||||
|
*/
|
||||||
|
value = JSON.parse(value as string);
|
||||||
|
} catch (e) {
|
||||||
|
/*
|
||||||
|
* when value is "green, red", JSON.parse throws error
|
||||||
|
*/
|
||||||
|
const splitByComma = (value as string).split(",") || [];
|
||||||
|
|
||||||
|
value = splitByComma.map((s) => s.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isString(value) && (value as string).trim() === "") {
|
||||||
|
isValid = true;
|
||||||
|
parsed = [];
|
||||||
|
message = "";
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
if (value.every((val) => _.isString(val) || _.isFinite(val))) {
|
||||||
|
/*
|
||||||
|
* When value is ["green", "red"]
|
||||||
|
*/
|
||||||
|
if (hasUniqueValues(value as [])) {
|
||||||
|
isValid = true;
|
||||||
|
parsed = value;
|
||||||
|
message = "";
|
||||||
|
} else {
|
||||||
|
isValid = false;
|
||||||
|
parsed = [];
|
||||||
|
message = "values must be unique. Duplicate values found";
|
||||||
|
}
|
||||||
|
} else if (value.every(hasLabelValue)) {
|
||||||
|
/*
|
||||||
|
* When value is [{label: "green", value: "red"}]
|
||||||
|
*/
|
||||||
|
if (hasUniqueValues(value.map((val) => val.value) as [])) {
|
||||||
|
isValid = true;
|
||||||
|
parsed = value;
|
||||||
|
message = "";
|
||||||
|
} else {
|
||||||
|
isValid = false;
|
||||||
|
parsed = [];
|
||||||
|
message = "path:value must be unique. Duplicate values found";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* When value is [true, false], [undefined, undefined] etc.
|
||||||
|
*/
|
||||||
|
isValid = false;
|
||||||
|
parsed = [];
|
||||||
|
message =
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* When value is undefined, null, {} etc.
|
||||||
|
*/
|
||||||
|
isValid = false;
|
||||||
|
parsed = [];
|
||||||
|
message =
|
||||||
|
"value should match: Array<string | number> | Array<{label: string, value: string | number}>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isValid,
|
||||||
|
parsed,
|
||||||
|
messages: [message],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class MultiSelectWidget extends BaseWidget<
|
class MultiSelectWidget extends BaseWidget<
|
||||||
MultiSelectWidgetProps,
|
MultiSelectWidgetProps,
|
||||||
|
|
@ -66,7 +177,7 @@ class MultiSelectWidget extends BaseWidget<
|
||||||
EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
helpText: "Selects the option with value by default",
|
helpText: "Selects the option(s) with value by default",
|
||||||
propertyName: "defaultOptionValue",
|
propertyName: "defaultOptionValue",
|
||||||
label: "Default Value",
|
label: "Default Value",
|
||||||
controlType: "INPUT_TEXT",
|
controlType: "INPUT_TEXT",
|
||||||
|
|
@ -74,32 +185,13 @@ class MultiSelectWidget extends BaseWidget<
|
||||||
isBindProperty: true,
|
isBindProperty: true,
|
||||||
isTriggerProperty: false,
|
isTriggerProperty: false,
|
||||||
validation: {
|
validation: {
|
||||||
type: ValidationTypes.ARRAY,
|
type: ValidationTypes.FUNCTION,
|
||||||
params: {
|
params: {
|
||||||
unique: ["value"],
|
fn: defaultOptionValueValidation,
|
||||||
children: {
|
expected: {
|
||||||
type: ValidationTypes.OBJECT,
|
type: "Array of values",
|
||||||
params: {
|
example: `['option1', 'option2'] | [{ "label": "label1", "value": "value1" }]`,
|
||||||
required: true,
|
autocompleteDataType: AutocompleteDataType.ARRAY,
|
||||||
allowedKeys: [
|
|
||||||
{
|
|
||||||
name: "label",
|
|
||||||
type: ValidationTypes.TEXT,
|
|
||||||
params: {
|
|
||||||
default: "",
|
|
||||||
requiredKey: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "value",
|
|
||||||
type: ValidationTypes.TEXT,
|
|
||||||
params: {
|
|
||||||
default: "",
|
|
||||||
requiredKey: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -304,8 +396,8 @@ class MultiSelectWidget extends BaseWidget<
|
||||||
|
|
||||||
static getDerivedPropertiesMap() {
|
static getDerivedPropertiesMap() {
|
||||||
return {
|
return {
|
||||||
selectedOptionLabels: `{{ this.selectedOptions ? this.selectedOptions.map((o) => o.label ) : [] }}`,
|
selectedOptionLabels: `{{ this.selectedOptions ? this.selectedOptions.map((o) => _.isNil(o.label) ? o : o.label ) : [] }}`,
|
||||||
selectedOptionValues: `{{ this.selectedOptions ? this.selectedOptions.map((o) => o.value ) : [] }}`,
|
selectedOptionValues: `{{ this.selectedOptions ? this.selectedOptions.map((o) => _.isNil(o.value) ? o : o.value ) : [] }}`,
|
||||||
isValid: `{{this.isRequired ? !!this.selectedOptionValues && this.selectedOptionValues.length > 0 : true}}`,
|
isValid: `{{this.isRequired ? !!this.selectedOptionValues && this.selectedOptionValues.length > 0 : true}}`,
|
||||||
isDirty: `{{ ((array1, array2) => {if (array1.length === array2.length) {return !array1.map((o) => o.value).every(element => array2.includes(element));} return true;})(this.defaultOptionValue, this.selectedOptionValues); }}`,
|
isDirty: `{{ ((array1, array2) => {if (array1.length === array2.length) {return !array1.map((o) => o.value).every(element => array2.includes(element));} return true;})(this.defaultOptionValue, this.selectedOptionValues); }}`,
|
||||||
};
|
};
|
||||||
|
|
@ -329,7 +421,13 @@ class MultiSelectWidget extends BaseWidget<
|
||||||
const options = isArray(this.props.options) ? this.props.options : [];
|
const options = isArray(this.props.options) ? this.props.options : [];
|
||||||
const dropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
|
const dropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
|
||||||
const { componentWidth } = this.getComponentDimensions();
|
const { componentWidth } = this.getComponentDimensions();
|
||||||
|
const values: LabelValueType[] = this.props.selectedOptions
|
||||||
|
? this.props.selectedOptions.map((o) =>
|
||||||
|
isString(o) || isNumber(o)
|
||||||
|
? { label: o, value: o }
|
||||||
|
: { label: o.label, value: o.value },
|
||||||
|
)
|
||||||
|
: [];
|
||||||
return (
|
return (
|
||||||
<MultiSelectComponent
|
<MultiSelectComponent
|
||||||
allowSelectAll={this.props.allowSelectAll}
|
allowSelectAll={this.props.allowSelectAll}
|
||||||
|
|
@ -358,7 +456,7 @@ class MultiSelectWidget extends BaseWidget<
|
||||||
options={options}
|
options={options}
|
||||||
placeholder={this.props.placeholderText as string}
|
placeholder={this.props.placeholderText as string}
|
||||||
serverSideFiltering={this.props.serverSideFiltering}
|
serverSideFiltering={this.props.serverSideFiltering}
|
||||||
value={this.props.selectedOptions ?? []}
|
value={values}
|
||||||
widgetId={this.props.widgetId}
|
widgetId={this.props.widgetId}
|
||||||
width={componentWidth}
|
width={componentWidth}
|
||||||
/>
|
/>
|
||||||
|
|
@ -366,6 +464,10 @@ class MultiSelectWidget extends BaseWidget<
|
||||||
}
|
}
|
||||||
|
|
||||||
onOptionChange = (value: DefaultValueType) => {
|
onOptionChange = (value: DefaultValueType) => {
|
||||||
|
if (!this.props.isDirty) {
|
||||||
|
this.props.updateWidgetMetaProperty("isDirty", true);
|
||||||
|
}
|
||||||
|
|
||||||
this.props.updateWidgetMetaProperty("selectedOptions", value, {
|
this.props.updateWidgetMetaProperty("selectedOptions", value, {
|
||||||
triggerPropertyName: "onOptionChange",
|
triggerPropertyName: "onOptionChange",
|
||||||
dynamicString: this.props.onOptionChange,
|
dynamicString: this.props.onOptionChange,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
BlueprintCSSTransform,
|
BlueprintCSSTransform,
|
||||||
createGlobalStyle,
|
createGlobalStyle,
|
||||||
} from "constants/DefaultTheme";
|
} from "constants/DefaultTheme";
|
||||||
|
import { isEmptyOrNill } from ".";
|
||||||
|
|
||||||
export const TextLabelWrapper = styled.div<{
|
export const TextLabelWrapper = styled.div<{
|
||||||
compactMode: boolean;
|
compactMode: boolean;
|
||||||
|
|
@ -145,7 +146,8 @@ export const StyledSingleDropDown = styled(SingleDropDown)<{
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 1;
|
-webkit-line-clamp: 1;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
color: ${(props) => (props.value ? Colors.GREY_10 : Colors.GREY_6)};
|
color: ${(props) =>
|
||||||
|
!isEmptyOrNill(props.value) ? Colors.GREY_10 : Colors.GREY_6};
|
||||||
}
|
}
|
||||||
&& {
|
&& {
|
||||||
.${Classes.ICON} {
|
.${Classes.ICON} {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { ComponentProps } from "widgets/BaseComponent";
|
||||||
import { MenuItem, Button, Classes } from "@blueprintjs/core";
|
import { MenuItem, Button, Classes } from "@blueprintjs/core";
|
||||||
import { DropdownOption } from "../constants";
|
import { DropdownOption } from "../constants";
|
||||||
import { IItemRendererProps } from "@blueprintjs/select";
|
import { IItemRendererProps } from "@blueprintjs/select";
|
||||||
import _ from "lodash";
|
import { debounce, findIndex, isEmpty, isNil } from "lodash";
|
||||||
import "../../../../node_modules/@blueprintjs/select/lib/css/blueprint-select.css";
|
import "../../../../node_modules/@blueprintjs/select/lib/css/blueprint-select.css";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
import { TextSize } from "constants/WidgetConstants";
|
import { TextSize } from "constants/WidgetConstants";
|
||||||
|
|
@ -19,6 +19,7 @@ import {
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { WidgetContainerDiff } from "widgets/WidgetUtils";
|
import { WidgetContainerDiff } from "widgets/WidgetUtils";
|
||||||
import Icon, { IconSize } from "components/ads/Icon";
|
import Icon, { IconSize } from "components/ads/Icon";
|
||||||
|
import { isString } from "../../../utils/helpers";
|
||||||
|
|
||||||
const FUSE_OPTIONS = {
|
const FUSE_OPTIONS = {
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
|
|
@ -29,6 +30,10 @@ const FUSE_OPTIONS = {
|
||||||
keys: ["label", "value"],
|
keys: ["label", "value"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isEmptyOrNill = (value: any) => {
|
||||||
|
return isNil(value) || (isString(value) && value === "");
|
||||||
|
};
|
||||||
|
|
||||||
const DEBOUNCE_TIMEOUT = 800;
|
const DEBOUNCE_TIMEOUT = 800;
|
||||||
|
|
||||||
interface SelectComponentState {
|
interface SelectComponentState {
|
||||||
|
|
@ -59,7 +64,7 @@ class SelectComponent extends React.Component<
|
||||||
|
|
||||||
handleActiveItemChange = (activeItem: DropdownOption | null) => {
|
handleActiveItemChange = (activeItem: DropdownOption | null) => {
|
||||||
// find new index from options
|
// find new index from options
|
||||||
const activeItemIndex = _.findIndex(this.props.options, [
|
const activeItemIndex = findIndex(this.props.options, [
|
||||||
"label",
|
"label",
|
||||||
activeItem?.label,
|
activeItem?.label,
|
||||||
]);
|
]);
|
||||||
|
|
@ -77,20 +82,21 @@ class SelectComponent extends React.Component<
|
||||||
widgetId,
|
widgetId,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
// active focused item
|
// active focused item
|
||||||
const activeItem = !_.isEmpty(this.props.options)
|
const activeItem = !isEmpty(this.props.options)
|
||||||
? this.props.options[this.state.activeItemIndex]
|
? this.props.options[this.state.activeItemIndex]
|
||||||
: undefined;
|
: undefined;
|
||||||
// get selected option label from selectedIndex
|
// get selected option label from selectedIndex
|
||||||
const selectedOption =
|
const selectedOption =
|
||||||
!_.isEmpty(this.props.options) &&
|
!isEmpty(this.props.options) &&
|
||||||
this.props.selectedIndex !== undefined &&
|
this.props.selectedIndex !== undefined &&
|
||||||
this.props.selectedIndex > -1
|
this.props.selectedIndex > -1
|
||||||
? this.props.options[this.props.selectedIndex].label
|
? this.props.options[this.props.selectedIndex].label
|
||||||
: this.props.label;
|
: this.props.label;
|
||||||
// for display selected option, there is no separate option to show placeholder
|
// for display selected option, there is no separate option to show placeholder
|
||||||
const value = selectedOption
|
const value =
|
||||||
? selectedOption
|
!isNil(selectedOption) && selectedOption !== ""
|
||||||
: this.props.placeholder || "-- Select --";
|
? selectedOption
|
||||||
|
: this.props.placeholder || "-- Select --";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownContainer compactMode={compactMode}>
|
<DropdownContainer compactMode={compactMode}>
|
||||||
|
|
@ -142,7 +148,7 @@ class SelectComponent extends React.Component<
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
if (!this.props.selectedIndex) return;
|
if (!this.props.selectedIndex) return;
|
||||||
return this.handleActiveItemChange(
|
return this.handleActiveItemChange(
|
||||||
this.props.options[this.props.selectedIndex as number],
|
this.props.options[this.props.selectedIndex],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
modifiers: {
|
modifiers: {
|
||||||
|
|
@ -160,7 +166,7 @@ class SelectComponent extends React.Component<
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
rightIcon={
|
rightIcon={
|
||||||
<StyledDiv>
|
<StyledDiv>
|
||||||
{this.props.value ? (
|
{!isEmptyOrNill(this.props.value) ? (
|
||||||
<Icon
|
<Icon
|
||||||
className="dropdown-icon cancel-icon"
|
className="dropdown-icon cancel-icon"
|
||||||
fillColor={
|
fillColor={
|
||||||
|
|
@ -201,7 +207,7 @@ class SelectComponent extends React.Component<
|
||||||
};
|
};
|
||||||
|
|
||||||
isOptionSelected = (selectedOption: DropdownOption) => {
|
isOptionSelected = (selectedOption: DropdownOption) => {
|
||||||
const optionIndex = _.findIndex(this.props.options, (option) => {
|
const optionIndex = findIndex(this.props.options, (option) => {
|
||||||
return option.value === selectedOption.value;
|
return option.value === selectedOption.value;
|
||||||
});
|
});
|
||||||
return optionIndex === this.props.selectedIndex;
|
return optionIndex === this.props.selectedIndex;
|
||||||
|
|
@ -211,7 +217,7 @@ class SelectComponent extends React.Component<
|
||||||
if (!this.props.serverSideFiltering) return;
|
if (!this.props.serverSideFiltering) return;
|
||||||
return this.serverSideSearch(filterValue);
|
return this.serverSideSearch(filterValue);
|
||||||
};
|
};
|
||||||
serverSideSearch = _.debounce((filterValue: string) => {
|
serverSideSearch = debounce((filterValue: string) => {
|
||||||
this.props.onFilterChange(filterValue);
|
this.props.onFilterChange(filterValue);
|
||||||
}, DEBOUNCE_TIMEOUT);
|
}, DEBOUNCE_TIMEOUT);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ export const CONFIG = {
|
||||||
],
|
],
|
||||||
serverSideFiltering: false,
|
serverSideFiltering: false,
|
||||||
widgetName: "Select",
|
widgetName: "Select",
|
||||||
defaultOptionValue: { label: "Green", value: "GREEN" },
|
defaultOptionValue: "GREEN",
|
||||||
version: 1,
|
version: 1,
|
||||||
isFilterable: false,
|
isFilterable: true,
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
animateLoading: true,
|
animateLoading: true,
|
||||||
|
|
|
||||||
109
app/client/src/widgets/SelectWidget/widget/index.test.tsx
Normal file
109
app/client/src/widgets/SelectWidget/widget/index.test.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
import _ from "lodash";
|
||||||
|
import { SelectWidgetProps, defaultOptionValueValidation } from ".";
|
||||||
|
|
||||||
|
describe("defaultOptionValueValidation - ", () => {
|
||||||
|
it("should get tested with simple string", () => {
|
||||||
|
const input = "";
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as SelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: "",
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with simple string", () => {
|
||||||
|
const input = "green";
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as SelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: "green",
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with plain object", () => {
|
||||||
|
const input = {
|
||||||
|
label: "green",
|
||||||
|
value: "green",
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as SelectWidgetProps, _),
|
||||||
|
).toEqual({
|
||||||
|
isValid: true,
|
||||||
|
parsed: {
|
||||||
|
label: "green",
|
||||||
|
value: "green",
|
||||||
|
},
|
||||||
|
messages: [""],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get tested with invalid values", () => {
|
||||||
|
const testValues = [
|
||||||
|
[
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: {},
|
||||||
|
messages: [
|
||||||
|
`value does not evaluate to type: string | { "label": "label1", "value": "value1" }`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: {},
|
||||||
|
messages: [
|
||||||
|
`value does not evaluate to type: string | { "label": "label1", "value": "value1" }`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: {},
|
||||||
|
messages: [
|
||||||
|
`value does not evaluate to type: string | { "label": "label1", "value": "value1" }`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: {},
|
||||||
|
messages: [
|
||||||
|
`value does not evaluate to type: string | { "label": "label1", "value": "value1" }`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
label: "green",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isValid: false,
|
||||||
|
parsed: {},
|
||||||
|
messages: [
|
||||||
|
`value does not evaluate to type: string | { "label": "label1", "value": "value1" }`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
testValues.forEach(([input, expected]) => {
|
||||||
|
expect(
|
||||||
|
defaultOptionValueValidation(input, {} as SelectWidgetProps, _),
|
||||||
|
).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -3,13 +3,70 @@ import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget";
|
||||||
import { WidgetType } from "constants/WidgetConstants";
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
||||||
import SelectComponent from "../component";
|
import SelectComponent from "../component";
|
||||||
import _ from "lodash";
|
|
||||||
import { DropdownOption } from "../constants";
|
import { DropdownOption } from "../constants";
|
||||||
import { ValidationTypes } from "constants/WidgetValidation";
|
import {
|
||||||
|
ValidationResponse,
|
||||||
|
ValidationTypes,
|
||||||
|
} from "constants/WidgetValidation";
|
||||||
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
||||||
import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
|
import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
|
||||||
|
import { AutocompleteDataType } from "utils/autocomplete/TernServer";
|
||||||
|
import { findIndex, isArray, isNumber, isString } from "lodash";
|
||||||
|
|
||||||
|
export function defaultOptionValueValidation(
|
||||||
|
value: unknown,
|
||||||
|
props: SelectWidgetProps,
|
||||||
|
_: any,
|
||||||
|
): ValidationResponse {
|
||||||
|
let isValid;
|
||||||
|
let parsed;
|
||||||
|
let message = "";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to check if the object has `label` and `value`
|
||||||
|
*/
|
||||||
|
const hasLabelValue = (obj: any) => {
|
||||||
|
return (
|
||||||
|
_.isPlainObject(value) &&
|
||||||
|
obj.hasOwnProperty("label") &&
|
||||||
|
obj.hasOwnProperty("value") &&
|
||||||
|
_.isString(obj.label) &&
|
||||||
|
(_.isString(obj.value) || _.isFinite(obj.value))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When value is "{label: 'green', value: 'green'}"
|
||||||
|
*/
|
||||||
|
if (typeof value === "string") {
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isString(value) || _.isFinite(value) || hasLabelValue(value)) {
|
||||||
|
/*
|
||||||
|
* When value is "", "green", 444, {label: "green", value: "green"}
|
||||||
|
*/
|
||||||
|
isValid = true;
|
||||||
|
parsed = value;
|
||||||
|
} else {
|
||||||
|
isValid = false;
|
||||||
|
parsed = {};
|
||||||
|
message = `value does not evaluate to type: string | { "label": "label1", "value": "value1" }`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isValid,
|
||||||
|
parsed,
|
||||||
|
messages: [message],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
|
constructor(props: SelectWidgetProps) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
static getPropertyPaneConfig() {
|
static getPropertyPaneConfig() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
@ -21,7 +78,7 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
propertyName: "options",
|
propertyName: "options",
|
||||||
label: "Options",
|
label: "Options",
|
||||||
controlType: "INPUT_TEXT",
|
controlType: "INPUT_TEXT",
|
||||||
placeholderText: '[{ "label": "Option1", "value": "Option2" }]',
|
placeholderText: '[{ "label": "label1", "value": "value1" }]',
|
||||||
isBindProperty: true,
|
isBindProperty: true,
|
||||||
isTriggerProperty: false,
|
isTriggerProperty: false,
|
||||||
validation: {
|
validation: {
|
||||||
|
|
@ -60,34 +117,24 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
{
|
{
|
||||||
helpText: "Selects the option with value by default",
|
helpText: "Selects the option with value by default",
|
||||||
propertyName: "defaultOptionValue",
|
propertyName: "defaultOptionValue",
|
||||||
label: "Default Option",
|
label: "Default Value",
|
||||||
controlType: "INPUT_TEXT",
|
controlType: "INPUT_TEXT",
|
||||||
placeholderText: '{ "label": "Option1", "value": "Option2" }',
|
placeholderText: '{ "label": "label1", "value": "value1" }',
|
||||||
isBindProperty: true,
|
isBindProperty: true,
|
||||||
isTriggerProperty: false,
|
isTriggerProperty: false,
|
||||||
validation: {
|
validation: {
|
||||||
type: ValidationTypes.OBJECT,
|
type: ValidationTypes.FUNCTION,
|
||||||
params: {
|
params: {
|
||||||
allowedKeys: [
|
fn: defaultOptionValueValidation,
|
||||||
{
|
expected: {
|
||||||
name: "label",
|
type: 'value1 or { "label": "label1", "value": "value1" }',
|
||||||
type: ValidationTypes.TEXT,
|
example: `value1 | { "label": "label1", "value": "value1" }`,
|
||||||
params: {
|
autocompleteDataType: AutocompleteDataType.STRING,
|
||||||
default: "",
|
},
|
||||||
requiredKey: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "value",
|
|
||||||
type: ValidationTypes.TEXT,
|
|
||||||
params: {
|
|
||||||
default: "",
|
|
||||||
requiredKey: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
evaluationSubstitutionType:
|
||||||
|
EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
helpText: "Sets a Label Text",
|
helpText: "Sets a Label Text",
|
||||||
|
|
@ -273,57 +320,49 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedPropertiesMap() {
|
|
||||||
return {
|
|
||||||
isValid: `{{this.isRequired ? !!this.selectedOptionValue || this.selectedOptionValue === 0 : true}}`,
|
|
||||||
selectedOptionLabel: `{{ this.optionValue.label ?? this.optionValue.value }}`,
|
|
||||||
selectedOptionValue: `{{ this.optionValue.value }}`,
|
|
||||||
isDirty: `{{ this.optionValue.value !== this.defaultValue.value }}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static getDefaultPropertiesMap(): Record<string, string> {
|
static getDefaultPropertiesMap(): Record<string, string> {
|
||||||
return {
|
return {
|
||||||
defaultValue: "defaultOptionValue",
|
defaultValue: "defaultOptionValue",
|
||||||
optionValue: "defaultOptionValue",
|
value: "defaultOptionValue",
|
||||||
|
label: "defaultOptionValue",
|
||||||
|
filterText: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static getMetaPropertiesMap(): Record<string, any> {
|
static getMetaPropertiesMap(): Record<string, any> {
|
||||||
return {
|
return {
|
||||||
defaultValue: undefined,
|
value: undefined,
|
||||||
optionValue: undefined,
|
label: undefined,
|
||||||
|
filterText: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDerivedPropertiesMap() {
|
||||||
|
return {
|
||||||
|
isValid: `{{this.isRequired ? !!this.selectedOptionValue || this.selectedOptionValue === 0 : true}}`,
|
||||||
|
selectedOptionLabel: `{{(()=>{const label = _.isPlainObject(this.label) ? this.label?.label : this.label; return label; })()}}`,
|
||||||
|
selectedOptionValue: `{{(()=>{const value = _.isPlainObject(this.value) ? this.value?.value : this.value; return value; })()}}`,
|
||||||
|
isDirty: `{{ this.value !== this.defaultValue }}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
super.componentDidMount();
|
||||||
this.changeSelectedOption();
|
this.changeSelectedOption();
|
||||||
}
|
}
|
||||||
componentDidUpdate(prevProps: SelectWidgetProps): void {
|
|
||||||
// removing selectedOptionValue if defaultValueChanges
|
|
||||||
if (
|
|
||||||
prevProps.defaultOptionValue?.value !==
|
|
||||||
this.props.defaultOptionValue?.value ||
|
|
||||||
prevProps.option !== this.props.option
|
|
||||||
) {
|
|
||||||
this.changeSelectedOption();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
changeSelectedOption = () => {
|
isStringOrNumber = (value: any): value is string | number =>
|
||||||
this.props.updateWidgetMetaProperty("optionValue", this.props.optionValue);
|
isString(value) || isNumber(value);
|
||||||
};
|
|
||||||
|
|
||||||
getPageView() {
|
getPageView() {
|
||||||
const options = _.isArray(this.props.options) ? this.props.options : [];
|
const options = isArray(this.props.options) ? this.props.options : [];
|
||||||
const isInvalid =
|
const isInvalid =
|
||||||
"isValid" in this.props && !this.props.isValid && !!this.props.isDirty;
|
"isValid" in this.props && !this.props.isValid && !!this.props.isDirty;
|
||||||
const dropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
|
const dropDownWidth = MinimumPopupRows * this.props.parentColumnSpace;
|
||||||
|
|
||||||
const selectedIndex = _.findIndex(this.props.options, {
|
const selectedIndex = findIndex(this.props.options, {
|
||||||
value: this.props.selectedOptionValue,
|
value: this.props.selectedOptionValue,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { componentHeight, componentWidth } = this.getComponentDimensions();
|
const { componentHeight, componentWidth } = this.getComponentDimensions();
|
||||||
return (
|
return (
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
|
|
@ -342,7 +381,7 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
isFilterable={this.props.isFilterable}
|
isFilterable={this.props.isFilterable}
|
||||||
isLoading={this.props.isLoading}
|
isLoading={this.props.isLoading}
|
||||||
isValid={this.props.isValid}
|
isValid={this.props.isValid}
|
||||||
label={this.props.optionValue?.label}
|
label={this.props.selectedOptionLabel}
|
||||||
labelStyle={this.props.labelStyle}
|
labelStyle={this.props.labelStyle}
|
||||||
labelText={this.props.labelText}
|
labelText={this.props.labelText}
|
||||||
labelTextColor={this.props.labelTextColor}
|
labelTextColor={this.props.labelTextColor}
|
||||||
|
|
@ -353,7 +392,7 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
placeholder={this.props.placeholderText}
|
placeholder={this.props.placeholderText}
|
||||||
selectedIndex={selectedIndex > -1 ? selectedIndex : undefined}
|
selectedIndex={selectedIndex > -1 ? selectedIndex : undefined}
|
||||||
serverSideFiltering={this.props.serverSideFiltering}
|
serverSideFiltering={this.props.serverSideFiltering}
|
||||||
value={this.props.optionValue?.value}
|
value={this.props.selectedOptionValue}
|
||||||
widgetId={this.props.widgetId}
|
widgetId={this.props.widgetId}
|
||||||
width={componentWidth}
|
width={componentWidth}
|
||||||
/>
|
/>
|
||||||
|
|
@ -369,9 +408,11 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
isChanged = !(this.props.selectedOptionValue === selectedOption.value);
|
isChanged = !(this.props.selectedOptionValue === selectedOption.value);
|
||||||
}
|
}
|
||||||
if (isChanged) {
|
if (isChanged) {
|
||||||
this.props.updateWidgetMetaProperty("optionValue", selectedOption, {
|
this.props.updateWidgetMetaProperty("label", selectedOption.label ?? "");
|
||||||
|
|
||||||
|
this.props.updateWidgetMetaProperty("value", selectedOption.value ?? "", {
|
||||||
triggerPropertyName: "onOptionChange",
|
triggerPropertyName: "onOptionChange",
|
||||||
dynamicString: this.props.onOptionChange as string,
|
dynamicString: this.props.onOptionChange,
|
||||||
event: {
|
event: {
|
||||||
type: EventType.ON_OPTION_CHANGE,
|
type: EventType.ON_OPTION_CHANGE,
|
||||||
},
|
},
|
||||||
|
|
@ -379,16 +420,29 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
changeSelectedOption = () => {
|
||||||
|
const label = this.isStringOrNumber(this.props.label)
|
||||||
|
? this.props.label
|
||||||
|
: this.props.label?.label;
|
||||||
|
const value = this.isStringOrNumber(this.props.value)
|
||||||
|
? this.props.value
|
||||||
|
: this.props.value?.value;
|
||||||
|
this.props.updateWidgetMetaProperty("value", value);
|
||||||
|
this.props.updateWidgetMetaProperty("label", label);
|
||||||
|
};
|
||||||
|
|
||||||
onFilterChange = (value: string) => {
|
onFilterChange = (value: string) => {
|
||||||
this.props.updateWidgetMetaProperty("filterText", value);
|
this.props.updateWidgetMetaProperty("filterText", value);
|
||||||
|
|
||||||
super.executeAction({
|
if (this.props.onFilterUpdate && this.props.serverSideFiltering) {
|
||||||
triggerPropertyName: "onFilterUpdate",
|
super.executeAction({
|
||||||
dynamicString: this.props.onFilterUpdate,
|
triggerPropertyName: "onFilterUpdate",
|
||||||
event: {
|
dynamicString: this.props.onFilterUpdate,
|
||||||
type: EventType.ON_FILTER_UPDATE,
|
event: {
|
||||||
},
|
type: EventType.ON_FILTER_UPDATE,
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static getWidgetType(): WidgetType {
|
static getWidgetType(): WidgetType {
|
||||||
|
|
@ -398,13 +452,12 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
|
||||||
|
|
||||||
export interface SelectWidgetProps extends WidgetProps {
|
export interface SelectWidgetProps extends WidgetProps {
|
||||||
placeholderText?: string;
|
placeholderText?: string;
|
||||||
label?: string;
|
|
||||||
selectedIndex?: number;
|
selectedIndex?: number;
|
||||||
selectedOption: DropdownOption;
|
|
||||||
options?: DropdownOption[];
|
options?: DropdownOption[];
|
||||||
onOptionChange?: string;
|
onOptionChange?: string;
|
||||||
defaultOptionValue?: { label?: string; value?: string };
|
defaultOptionValue?: any;
|
||||||
value?: string;
|
value?: any;
|
||||||
|
label?: any;
|
||||||
isRequired: boolean;
|
isRequired: boolean;
|
||||||
isFilterable: boolean;
|
isFilterable: boolean;
|
||||||
defaultValue: string;
|
defaultValue: string;
|
||||||
|
|
|
||||||
|
|
@ -66,27 +66,20 @@ const WIDGET_CONFIG_MAP: WidgetTypeConfigMap = {
|
||||||
},
|
},
|
||||||
metaProperties: {},
|
metaProperties: {},
|
||||||
},
|
},
|
||||||
DROP_DOWN_WIDGET: {
|
SELECT_WIDGET: {
|
||||||
defaultProperties: {
|
defaultProperties: {
|
||||||
selectedOptionValue: "defaultOptionValue",
|
selectedOption: "defaultOptionValue",
|
||||||
selectedOptionValueArr: "defaultOptionValue",
|
filterText: "",
|
||||||
},
|
},
|
||||||
derivedProperties: {
|
derivedProperties: {
|
||||||
isValid:
|
selectedOptionLabel: `{{_.isPlainObject(this.selectedOption) ? this.selectedOption?.label : this.selectedOption}}`,
|
||||||
"{{this.isRequired ? this.selectionType === 'SINGLE_SELECT' ? !!this.selectedOption : !!this.selectedIndexArr && this.selectedIndexArr.length > 0 : true}}",
|
selectedOptionValue: `{{_.isPlainObject(this.selectedOption) ? this.selectedOption?.value : this.selectedOption}}`,
|
||||||
selectedOption:
|
isValid: `{{this.isRequired ? !!this.selectedOptionValue || this.selectedOptionValue === 0 : true}}`,
|
||||||
"{{ this.selectionType === 'SINGLE_SELECT' ? _.find(this.options, { value: this.selectedOptionValue }) : undefined}}",
|
},
|
||||||
selectedOptionArr:
|
metaProperties: {
|
||||||
'{{this.selectionType === "MULTI_SELECT" ? this.options.filter(opt => _.includes(this.selectedOptionValueArr, opt.value)) : undefined}}',
|
selectedOption: undefined,
|
||||||
selectedIndex:
|
filterText: "",
|
||||||
"{{ _.findIndex(this.options, { value: this.selectedOption.value } ) }}",
|
|
||||||
selectedIndexArr:
|
|
||||||
"{{ this.selectedOptionValueArr.map(o => _.findIndex(this.options, { value: o })) }}",
|
|
||||||
value:
|
|
||||||
"{{ this.selectionType === 'SINGLE_SELECT' ? this.selectedOptionValue : this.selectedOptionValueArr }}",
|
|
||||||
selectedOptionValues: "{{ this.selectedOptionValueArr }}",
|
|
||||||
},
|
},
|
||||||
metaProperties: {},
|
|
||||||
},
|
},
|
||||||
RADIO_GROUP_WIDGET: {
|
RADIO_GROUP_WIDGET: {
|
||||||
defaultProperties: {
|
defaultProperties: {
|
||||||
|
|
@ -278,25 +271,25 @@ const mockDerived = jest.spyOn(WidgetFactory, "getWidgetDerivedPropertiesMap");
|
||||||
const dependencyMap = {
|
const dependencyMap = {
|
||||||
Dropdown1: [
|
Dropdown1: [
|
||||||
"Dropdown1.defaultOptionValue",
|
"Dropdown1.defaultOptionValue",
|
||||||
|
"Dropdown1.filterText",
|
||||||
"Dropdown1.isValid",
|
"Dropdown1.isValid",
|
||||||
"Dropdown1.selectedIndex",
|
"Dropdown1.meta",
|
||||||
"Dropdown1.selectedIndexArr",
|
|
||||||
"Dropdown1.selectedOption",
|
"Dropdown1.selectedOption",
|
||||||
"Dropdown1.selectedOptionArr",
|
"Dropdown1.selectedOptionLabel",
|
||||||
"Dropdown1.selectedOptionValue",
|
"Dropdown1.selectedOptionValue",
|
||||||
"Dropdown1.selectedOptionValueArr",
|
|
||||||
"Dropdown1.selectedOptionValues",
|
|
||||||
"Dropdown1.value",
|
|
||||||
],
|
],
|
||||||
"Dropdown1.isValid": [],
|
"Dropdown1.isValid": [],
|
||||||
"Dropdown1.selectedIndex": [],
|
"Dropdown1.filterText": ["Dropdown1.meta.filterText"],
|
||||||
"Dropdown1.selectedIndexArr": [],
|
"Dropdown1.meta": [
|
||||||
"Dropdown1.selectedOption": [],
|
"Dropdown1.meta.filterText",
|
||||||
"Dropdown1.selectedOptionArr": [],
|
"Dropdown1.meta.selectedOption",
|
||||||
"Dropdown1.selectedOptionValue": ["Dropdown1.defaultOptionValue"],
|
],
|
||||||
"Dropdown1.selectedOptionValueArr": ["Dropdown1.defaultOptionValue"],
|
"Dropdown1.selectedOption": [
|
||||||
"Dropdown1.selectedOptionValues": [],
|
"Dropdown1.defaultOptionValue",
|
||||||
"Dropdown1.value": [],
|
"Dropdown1.meta.selectedOption",
|
||||||
|
],
|
||||||
|
"Dropdown1.selectedOptionLabel": [],
|
||||||
|
"Dropdown1.selectedOptionValue": [],
|
||||||
Table1: [
|
Table1: [
|
||||||
"Table1.defaultSearchText",
|
"Table1.defaultSearchText",
|
||||||
"Table1.defaultSelectedRow",
|
"Table1.defaultSelectedRow",
|
||||||
|
|
@ -394,7 +387,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
value: "valueTest2",
|
value: "valueTest2",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
type: "DROP_DOWN_WIDGET",
|
type: "SELECT_WIDGET",
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
),
|
),
|
||||||
|
|
@ -492,7 +485,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
value: "valueTest2",
|
value: "valueTest2",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
type: "DROP_DOWN_WIDGET",
|
type: "SELECT_WIDGET",
|
||||||
bindingPaths: {
|
bindingPaths: {
|
||||||
options: EvaluationSubstitutionType.TEMPLATE,
|
options: EvaluationSubstitutionType.TEMPLATE,
|
||||||
defaultOptionValue: EvaluationSubstitutionType.TEMPLATE,
|
defaultOptionValue: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -501,11 +494,8 @@ describe("DataTreeEvaluator", () => {
|
||||||
isDisabled: EvaluationSubstitutionType.TEMPLATE,
|
isDisabled: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isValid: EvaluationSubstitutionType.TEMPLATE,
|
isValid: EvaluationSubstitutionType.TEMPLATE,
|
||||||
selectedOption: EvaluationSubstitutionType.TEMPLATE,
|
selectedOption: EvaluationSubstitutionType.TEMPLATE,
|
||||||
selectedOptionArr: EvaluationSubstitutionType.TEMPLATE,
|
selectedOptionValue: EvaluationSubstitutionType.TEMPLATE,
|
||||||
selectedIndex: EvaluationSubstitutionType.TEMPLATE,
|
selectedOptionLabel: EvaluationSubstitutionType.TEMPLATE,
|
||||||
selectedIndexArr: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
value: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
selectedOptionValues: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export const WidgetTypeFactories: Record<string, any> = {
|
||||||
DATE_PICKER_WIDGET: OldDatepickerFactory,
|
DATE_PICKER_WIDGET: OldDatepickerFactory,
|
||||||
DATE_PICKER_WIDGET2: DatepickerFactory,
|
DATE_PICKER_WIDGET2: DatepickerFactory,
|
||||||
TABLE_WIDGET: TableFactory,
|
TABLE_WIDGET: TableFactory,
|
||||||
DROP_DOWN_WIDGET: DropdownFactory,
|
SELECT_WIDGET: DropdownFactory,
|
||||||
CHECKBOX_WIDGET: CheckboxFactory,
|
CHECKBOX_WIDGET: CheckboxFactory,
|
||||||
RADIO_GROUP_WIDGET: RadiogroupFactory,
|
RADIO_GROUP_WIDGET: RadiogroupFactory,
|
||||||
TABS_WIDGET: TabsFactory,
|
TABS_WIDGET: TabsFactory,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import com.appsmith.server.domains.CommentThread;
|
||||||
import com.appsmith.external.models.Datasource;
|
import com.appsmith.external.models.Datasource;
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
import com.appsmith.server.domains.Page;
|
import com.appsmith.server.domains.Page;
|
||||||
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
|
@ -74,13 +75,15 @@ public enum AclPermission {
|
||||||
READ_DATASOURCES("read:datasources", Datasource.class),
|
READ_DATASOURCES("read:datasources", Datasource.class),
|
||||||
EXECUTE_DATASOURCES("execute:datasources", Datasource.class),
|
EXECUTE_DATASOURCES("execute:datasources", Datasource.class),
|
||||||
|
|
||||||
COMMENT_ON_THREAD("canComment:commentThreads", CommentThread.class),
|
COMMENT_ON_THREADS("canComment:commentThreads", CommentThread.class),
|
||||||
READ_THREAD("read:commentThreads", CommentThread.class),
|
READ_THREADS("read:commentThreads", CommentThread.class),
|
||||||
MANAGE_THREAD("manage:commentThreads", CommentThread.class),
|
MANAGE_THREADS("manage:commentThreads", CommentThread.class),
|
||||||
|
|
||||||
READ_COMMENT("read:comments", Comment.class),
|
READ_COMMENTS("read:comments", Comment.class),
|
||||||
MANAGE_COMMENT("manage:comments", Comment.class),
|
MANAGE_COMMENTS("manage:comments", Comment.class),
|
||||||
|
|
||||||
|
READ_THEMES("read:themes", Theme.class),
|
||||||
|
MANAGE_THEMES("manage:themes", Theme.class),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.appsmith.server.acl.AclPermission.COMMENT_ON_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.COMMENT_ON_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.COMMENT_ON_THREAD;
|
import static com.appsmith.server.acl.AclPermission.COMMENT_ON_THREADS;
|
||||||
import static com.appsmith.server.acl.AclPermission.EXECUTE_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.EXECUTE_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
|
import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES;
|
||||||
import static com.appsmith.server.acl.AclPermission.EXPORT_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.EXPORT_APPLICATIONS;
|
||||||
|
|
@ -30,6 +30,7 @@ import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_ORGANIZATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_ORGANIZATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.MANAGE_THEMES;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_USERS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_USERS;
|
||||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_EXPORT_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_EXPORT_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
|
||||||
|
|
@ -38,11 +39,12 @@ import static com.appsmith.server.acl.AclPermission.ORGANIZATION_READ_APPLICATIO
|
||||||
import static com.appsmith.server.acl.AclPermission.PUBLISH_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.PUBLISH_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_COMMENT;
|
import static com.appsmith.server.acl.AclPermission.READ_COMMENTS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
|
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_ORGANIZATIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_ORGANIZATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_THREAD;
|
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.READ_THREADS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_USERS;
|
import static com.appsmith.server.acl.AclPermission.READ_USERS;
|
||||||
import static com.appsmith.server.acl.AclPermission.USER_MANAGE_ORGANIZATIONS;
|
import static com.appsmith.server.acl.AclPermission.USER_MANAGE_ORGANIZATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.USER_READ_ORGANIZATIONS;
|
import static com.appsmith.server.acl.AclPermission.USER_READ_ORGANIZATIONS;
|
||||||
|
|
@ -81,6 +83,7 @@ public class PolicyGeneratorCE {
|
||||||
createPagePolicyGraph();
|
createPagePolicyGraph();
|
||||||
createActionPolicyGraph();
|
createActionPolicyGraph();
|
||||||
createCommentPolicyGraph();
|
createCommentPolicyGraph();
|
||||||
|
createThemePolicyGraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -142,11 +145,17 @@ public class PolicyGeneratorCE {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCommentPolicyGraph() {
|
private void createCommentPolicyGraph() {
|
||||||
hierarchyGraph.addEdge(COMMENT_ON_APPLICATIONS, COMMENT_ON_THREAD);
|
hierarchyGraph.addEdge(COMMENT_ON_APPLICATIONS, COMMENT_ON_THREADS);
|
||||||
|
|
||||||
lateralGraph.addEdge(COMMENT_ON_THREAD, READ_THREAD);
|
lateralGraph.addEdge(COMMENT_ON_THREADS, READ_THREADS);
|
||||||
|
|
||||||
hierarchyGraph.addEdge(COMMENT_ON_THREAD, READ_COMMENT);
|
hierarchyGraph.addEdge(COMMENT_ON_THREADS, READ_COMMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createThemePolicyGraph() {
|
||||||
|
hierarchyGraph.addEdge(MANAGE_APPLICATIONS, MANAGE_THEMES);
|
||||||
|
hierarchyGraph.addEdge(READ_APPLICATIONS, READ_THEMES);
|
||||||
|
lateralGraph.addEdge(MANAGE_THEMES, READ_THEMES);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Policy> getLateralPolicies(AclPermission permission, Set<String> userNames, Class<? extends BaseDomain> destinationEntity) {
|
public Set<Policy> getLateralPolicies(AclPermission permission, Set<String> userNames, Class<? extends BaseDomain> destinationEntity) {
|
||||||
|
|
|
||||||
|
|
@ -106,5 +106,6 @@ public class FieldName {
|
||||||
public static final String ACTION_LIST = "actionList";
|
public static final String ACTION_LIST = "actionList";
|
||||||
public static final String ACTION_COLLECTION_LIST = "actionCollectionList";
|
public static final String ACTION_COLLECTION_LIST = "actionCollectionList";
|
||||||
public static final String DECRYPTED_FIELDS = "decryptedFields";
|
public static final String DECRYPTED_FIELDS = "decryptedFields";
|
||||||
|
public static final String THEME = "theme";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,23 @@ import com.appsmith.server.constants.Url;
|
||||||
import com.appsmith.server.domains.ApplicationMode;
|
import com.appsmith.server.domains.ApplicationMode;
|
||||||
import com.appsmith.server.domains.Theme;
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.dtos.ResponseDTO;
|
import com.appsmith.server.dtos.ResponseDTO;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.services.ThemeService;
|
import com.appsmith.server.services.ThemeService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PatchMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequestMapping(Url.THEME_URL)
|
@RequestMapping(Url.THEME_URL)
|
||||||
|
|
@ -24,15 +29,38 @@ public class ThemeControllerCE extends BaseController<ThemeService, Theme, Strin
|
||||||
super(themeService);
|
super(themeService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ResponseDTO<Theme>> create(Theme resource, String originHeader, ServerWebExchange exchange) {
|
||||||
|
throw new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("applications/{applicationId}")
|
@GetMapping("applications/{applicationId}")
|
||||||
public Mono<ResponseDTO<Theme>> getThemes(@PathVariable String applicationId, @RequestParam(required = false, defaultValue = "EDIT") ApplicationMode mode) {
|
public Mono<ResponseDTO<List<Theme>>> getApplicationThemes(@PathVariable String applicationId) {
|
||||||
|
return service.getApplicationThemes(applicationId).collectList()
|
||||||
|
.map(themes -> new ResponseDTO<>(HttpStatus.OK.value(), themes, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("applications/{applicationId}/current")
|
||||||
|
public Mono<ResponseDTO<Theme>> getCurrentTheme(@PathVariable String applicationId, @RequestParam(required = false, defaultValue = "EDIT") ApplicationMode mode) {
|
||||||
return service.getApplicationTheme(applicationId, mode)
|
return service.getApplicationTheme(applicationId, mode)
|
||||||
.map(theme -> new ResponseDTO<>(HttpStatus.OK.value(), theme, null));
|
.map(theme -> new ResponseDTO<>(HttpStatus.OK.value(), theme, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("applications/{applicationId}")
|
@PutMapping("applications/{applicationId}")
|
||||||
public Mono<ResponseDTO<Theme>> updateTheme(@PathVariable String applicationId, @Valid @RequestBody Theme resource) {
|
public Mono<ResponseDTO<Theme>> updateTheme(@PathVariable String applicationId, @Valid @RequestBody Theme resource) {
|
||||||
return service.updateTheme(applicationId, resource)
|
return service.updateTheme(applicationId, resource)
|
||||||
.map(theme -> new ResponseDTO<>(HttpStatus.OK.value(), theme, null));
|
.map(theme -> new ResponseDTO<>(HttpStatus.OK.value(), theme, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PatchMapping("applications/{applicationId}")
|
||||||
|
public Mono<ResponseDTO<Theme>> publishCurrentTheme(@PathVariable String applicationId, @RequestBody Theme resource) {
|
||||||
|
return service.persistCurrentTheme(applicationId, resource)
|
||||||
|
.map(theme -> new ResponseDTO<>(HttpStatus.OK.value(), theme, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("{themeId}")
|
||||||
|
public Mono<ResponseDTO<Theme>> updateName(@PathVariable String themeId, @Valid @RequestBody Theme resource) {
|
||||||
|
return service.updateName(themeId, resource)
|
||||||
|
.map(theme -> new ResponseDTO<>(HttpStatus.OK.value(), theme, null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package com.appsmith.server.domains;
|
||||||
|
|
||||||
import com.appsmith.external.models.BaseDomain;
|
import com.appsmith.external.models.BaseDomain;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.google.gson.annotations.SerializedName;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
@ -11,7 +10,6 @@ import lombok.Setter;
|
||||||
import org.springframework.data.mongodb.core.mapping.Document;
|
import org.springframework.data.mongodb.core.mapping.Document;
|
||||||
|
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
|
@ -23,23 +21,15 @@ public class Theme extends BaseDomain {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String name;
|
private String name;
|
||||||
private Config config;
|
private String applicationId;
|
||||||
private Properties properties;
|
private String organizationId;
|
||||||
private Map<String, WidgetStyle> stylesheet;
|
private Object config;
|
||||||
|
private Object properties;
|
||||||
|
private Map<String, Object> stylesheet;
|
||||||
|
|
||||||
@JsonProperty("isSystemTheme") // manually setting property name to make sure it's compatible with Gson
|
@JsonProperty("isSystemTheme") // manually setting property name to make sure it's compatible with Gson
|
||||||
private boolean isSystemTheme = false; // should be false by default
|
private boolean isSystemTheme = false; // should be false by default
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
public static class Properties {
|
|
||||||
private Colors colors;
|
|
||||||
private BorderRadiusProperties borderRadius;
|
|
||||||
private BoxShadowProperties boxShadow;
|
|
||||||
private FontFamilyProperties fontFamily;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|
@ -47,87 +37,4 @@ public class Theme extends BaseDomain {
|
||||||
private String primaryColor;
|
private String primaryColor;
|
||||||
private String backgroundColor;
|
private String backgroundColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Config {
|
|
||||||
private Colors colors;
|
|
||||||
private BorderRadius borderRadius;
|
|
||||||
private BoxShadow boxShadow;
|
|
||||||
private FontFamily fontFamily;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class ResponsiveAttributes {
|
|
||||||
@JsonProperty("none")
|
|
||||||
@SerializedName("none")
|
|
||||||
private String noneValue;
|
|
||||||
|
|
||||||
@JsonProperty("DEFAULT")
|
|
||||||
@SerializedName("DEFAULT")
|
|
||||||
private String defaultValue;
|
|
||||||
|
|
||||||
@JsonProperty("md")
|
|
||||||
@SerializedName("md")
|
|
||||||
private String mdValue;
|
|
||||||
|
|
||||||
@JsonProperty("lg")
|
|
||||||
@SerializedName("lg")
|
|
||||||
private String lgValue;
|
|
||||||
|
|
||||||
@JsonProperty("xl")
|
|
||||||
@SerializedName("xl")
|
|
||||||
private String xlValue;
|
|
||||||
|
|
||||||
@JsonProperty("2xl")
|
|
||||||
@SerializedName("2xl")
|
|
||||||
private String doubleXlValue;
|
|
||||||
|
|
||||||
@JsonProperty("3xl")
|
|
||||||
@SerializedName("3xl")
|
|
||||||
private String tripleXlValue;
|
|
||||||
|
|
||||||
@JsonProperty("full")
|
|
||||||
@SerializedName("full")
|
|
||||||
private String fullValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class BorderRadius {
|
|
||||||
private ResponsiveAttributes appBorderRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class BoxShadow {
|
|
||||||
private ResponsiveAttributes appBoxShadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class FontFamily {
|
|
||||||
private List<String> appFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class FontFamilyProperties {
|
|
||||||
private String appFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class WidgetStyle {
|
|
||||||
private String backgroundColor;
|
|
||||||
private String borderRadius;
|
|
||||||
private String boxShadow;
|
|
||||||
private String primaryColor;
|
|
||||||
private String menuColor;
|
|
||||||
private String buttonColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class BorderRadiusProperties {
|
|
||||||
private String appBorderRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class BoxShadowProperties {
|
|
||||||
private String appBoxShadow;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.CommentThread;
|
import com.appsmith.server.domains.CommentThread;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.domains.NewPage;
|
import com.appsmith.server.domains.NewPage;
|
||||||
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.repositories.ActionCollectionRepository;
|
import com.appsmith.server.repositories.ActionCollectionRepository;
|
||||||
import com.appsmith.server.repositories.ApplicationRepository;
|
import com.appsmith.server.repositories.ApplicationRepository;
|
||||||
|
|
@ -17,9 +18,11 @@ import com.appsmith.server.repositories.CommentThreadRepository;
|
||||||
import com.appsmith.server.repositories.DatasourceRepository;
|
import com.appsmith.server.repositories.DatasourceRepository;
|
||||||
import com.appsmith.server.repositories.NewActionRepository;
|
import com.appsmith.server.repositories.NewActionRepository;
|
||||||
import com.appsmith.server.repositories.NewPageRepository;
|
import com.appsmith.server.repositories.NewPageRepository;
|
||||||
|
import com.appsmith.server.repositories.ThemeRepository;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
|
@ -35,6 +38,7 @@ import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
|
@ -47,6 +51,7 @@ public class PolicyUtils {
|
||||||
private final NewActionRepository newActionRepository;
|
private final NewActionRepository newActionRepository;
|
||||||
private final CommentThreadRepository commentThreadRepository;
|
private final CommentThreadRepository commentThreadRepository;
|
||||||
private final ActionCollectionRepository actionCollectionRepository;
|
private final ActionCollectionRepository actionCollectionRepository;
|
||||||
|
private final ThemeRepository themeRepository;
|
||||||
|
|
||||||
public <T extends BaseDomain> T addPoliciesToExistingObject(Map<String, Policy> policyMap, T obj) {
|
public <T extends BaseDomain> T addPoliciesToExistingObject(Map<String, Policy> policyMap, T obj) {
|
||||||
// Making a deep copy here so we don't modify the `policyMap` object.
|
// Making a deep copy here so we don't modify the `policyMap` object.
|
||||||
|
|
@ -231,12 +236,37 @@ public class PolicyUtils {
|
||||||
.saveAll(updatedPages));
|
.saveAll(updatedPages));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Flux<Theme> updateThemePolicies(Application application, Map<String, Policy> themePolicyMap, boolean addPolicyToObject) {
|
||||||
|
Flux<Theme> applicationThemes = themeRepository.getApplicationThemes(application.getId(), READ_THEMES);
|
||||||
|
if(StringUtils.hasLength(application.getEditModeThemeId())) {
|
||||||
|
applicationThemes = applicationThemes.concatWith(
|
||||||
|
themeRepository.findById(application.getEditModeThemeId(), READ_THEMES)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if(StringUtils.hasLength(application.getPublishedModeThemeId())) {
|
||||||
|
applicationThemes = applicationThemes.concatWith(
|
||||||
|
themeRepository.findById(application.getPublishedModeThemeId(), READ_THEMES)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return applicationThemes
|
||||||
|
.filter(theme -> !theme.isSystemTheme()) // skip the system themes
|
||||||
|
.map(theme -> {
|
||||||
|
if (addPolicyToObject) {
|
||||||
|
return addPoliciesToExistingObject(themePolicyMap, theme);
|
||||||
|
} else {
|
||||||
|
return removePoliciesFromExistingObject(themePolicyMap, theme);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collectList()
|
||||||
|
.flatMapMany(themeRepository::saveAll);
|
||||||
|
}
|
||||||
|
|
||||||
public Flux<CommentThread> updateCommentThreadPermissions(
|
public Flux<CommentThread> updateCommentThreadPermissions(
|
||||||
String applicationId, Map<String, Policy> commentThreadPolicyMap, String username, boolean addPolicyToObject) {
|
String applicationId, Map<String, Policy> commentThreadPolicyMap, String username, boolean addPolicyToObject) {
|
||||||
|
|
||||||
return
|
return
|
||||||
// fetch comment threads with read permissions
|
// fetch comment threads with read permissions
|
||||||
commentThreadRepository.findByApplicationId(applicationId, AclPermission.READ_THREAD)
|
commentThreadRepository.findByApplicationId(applicationId, AclPermission.READ_THREADS)
|
||||||
.switchIfEmpty(Mono.empty())
|
.switchIfEmpty(Mono.empty())
|
||||||
.map(thread -> {
|
.map(thread -> {
|
||||||
if(!Boolean.TRUE.equals(thread.getIsPrivate())) {
|
if(!Boolean.TRUE.equals(thread.getIsPrivate())) {
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ import static com.appsmith.server.acl.AclPermission.MAKE_PUBLIC_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_EXPORT_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_EXPORT_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_INVITE_USERS;
|
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_INVITE_USERS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
|
||||||
import static com.appsmith.server.constants.FieldName.DEFAULT_RESOURCES;
|
import static com.appsmith.server.constants.FieldName.DEFAULT_RESOURCES;
|
||||||
import static com.appsmith.server.constants.FieldName.DYNAMIC_TRIGGER_PATH_LIST;
|
import static com.appsmith.server.constants.FieldName.DYNAMIC_TRIGGER_PATH_LIST;
|
||||||
import static com.appsmith.server.helpers.CollectionUtils.isNullOrEmpty;
|
import static com.appsmith.server.helpers.CollectionUtils.isNullOrEmpty;
|
||||||
|
|
@ -4744,37 +4745,6 @@ public class DatabaseChangelog {
|
||||||
mongockTemplate.save(firestorePlugin);
|
mongockTemplate.save(firestorePlugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ChangeSet(order = "108", id = "create-system-themes", author = "")
|
|
||||||
public void createSystemThemes(MongockTemplate mongockTemplate) throws IOException {
|
|
||||||
Index uniqueApplicationIdIndex = new Index()
|
|
||||||
.on(fieldName(QTheme.theme.isSystemTheme), Sort.Direction.ASC)
|
|
||||||
.named("system_theme_index");
|
|
||||||
|
|
||||||
ensureIndexes(mongockTemplate, Theme.class, uniqueApplicationIdIndex);
|
|
||||||
|
|
||||||
final String themesJson = StreamUtils.copyToString(
|
|
||||||
new DefaultResourceLoader().getResource("system-themes.json").getInputStream(),
|
|
||||||
Charset.defaultCharset()
|
|
||||||
);
|
|
||||||
Theme[] themes = new Gson().fromJson(themesJson, Theme[].class);
|
|
||||||
|
|
||||||
Theme legacyTheme = null;
|
|
||||||
for (Theme theme : themes) {
|
|
||||||
theme.setSystemTheme(true);
|
|
||||||
Theme savedTheme = mongockTemplate.save(theme);
|
|
||||||
if(savedTheme.getName().equalsIgnoreCase(Theme.LEGACY_THEME_NAME)) {
|
|
||||||
legacyTheme = savedTheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// migrate all applications and set legacy theme to them in both mode
|
|
||||||
Update update = new Update().set(fieldName(QApplication.application.publishedModeThemeId), legacyTheme.getId())
|
|
||||||
.set(fieldName(QApplication.application.editModeThemeId), legacyTheme.getId());
|
|
||||||
mongockTemplate.updateMulti(
|
|
||||||
new Query(where(fieldName(QApplication.application.deleted)).is(false)), update, Application.class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method sets the key formData.aggregate.limit to 101 for all Mongo plugin actions.
|
* This method sets the key formData.aggregate.limit to 101 for all Mongo plugin actions.
|
||||||
* It iterates over each action id one by one to avoid out of memory error.
|
* It iterates over each action id one by one to avoid out of memory error.
|
||||||
|
|
@ -4817,6 +4787,11 @@ public class DatabaseChangelog {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ChangeSet(order = "108", id = "create-system-themes", author = "")
|
||||||
|
public void createSystemThemes(MongockTemplate mongockTemplate) throws IOException {
|
||||||
|
createSystemThemes2(mongockTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This migration adds a new field to Mongo aggregate command to set batchSize: formData.aggregate.limit. Its value
|
* This migration adds a new field to Mongo aggregate command to set batchSize: formData.aggregate.limit. Its value
|
||||||
* is set by this migration to 101 for all existing actions since this is the default `batchSize` used by
|
* is set by this migration to 101 for all existing actions since this is the default `batchSize` used by
|
||||||
|
|
@ -5025,4 +5000,75 @@ public class DatabaseChangelog {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adding this migration again because we've added permission to themes.
|
||||||
|
* Also there are couple of changes in the system theme properties.
|
||||||
|
* @param mongockTemplate
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@ChangeSet(order = "117", id = "create-system-themes-v2", author = "")
|
||||||
|
public void createSystemThemes2(MongockTemplate mongockTemplate) throws IOException {
|
||||||
|
Index systemThemeIndex = new Index()
|
||||||
|
.on(fieldName(QTheme.theme.isSystemTheme), Sort.Direction.ASC)
|
||||||
|
.named("system_theme_index")
|
||||||
|
.background();
|
||||||
|
|
||||||
|
Index applicationIdIndex = new Index()
|
||||||
|
.on(fieldName(QTheme.theme.applicationId), Sort.Direction.ASC)
|
||||||
|
.on(fieldName(QTheme.theme.deleted), Sort.Direction.ASC)
|
||||||
|
.named("application_id_index")
|
||||||
|
.background();
|
||||||
|
|
||||||
|
dropIndexIfExists(mongockTemplate, Theme.class, "system_theme_index");
|
||||||
|
dropIndexIfExists(mongockTemplate, Theme.class, "application_id_index");
|
||||||
|
ensureIndexes(mongockTemplate, Theme.class, systemThemeIndex, applicationIdIndex);
|
||||||
|
|
||||||
|
final String themesJson = StreamUtils.copyToString(
|
||||||
|
new DefaultResourceLoader().getResource("system-themes.json").getInputStream(),
|
||||||
|
Charset.defaultCharset()
|
||||||
|
);
|
||||||
|
Theme[] themes = new Gson().fromJson(themesJson, Theme[].class);
|
||||||
|
|
||||||
|
Theme legacyTheme = null;
|
||||||
|
boolean themeExists = false;
|
||||||
|
|
||||||
|
Policy policyWithCurrentPermission = Policy.builder().permission(READ_THEMES.getValue())
|
||||||
|
.users(Set.of(FieldName.ANONYMOUS_USER)).build();
|
||||||
|
|
||||||
|
for (Theme theme : themes) {
|
||||||
|
theme.setSystemTheme(true);
|
||||||
|
theme.setCreatedAt(Instant.now());
|
||||||
|
theme.setPolicies(Set.of(policyWithCurrentPermission));
|
||||||
|
Query query = new Query(Criteria.where(fieldName(QTheme.theme.name)).is(theme.getName())
|
||||||
|
.and(fieldName(QTheme.theme.isSystemTheme)).is(true));
|
||||||
|
|
||||||
|
Theme savedTheme = mongockTemplate.findOne(query, Theme.class);
|
||||||
|
if(savedTheme == null) { // this theme does not exist, create it
|
||||||
|
savedTheme = mongockTemplate.save(theme);
|
||||||
|
} else { // theme already found, update
|
||||||
|
themeExists = true;
|
||||||
|
savedTheme.setPolicies(theme.getPolicies());
|
||||||
|
savedTheme.setConfig(theme.getConfig());
|
||||||
|
savedTheme.setProperties(theme.getProperties());
|
||||||
|
savedTheme.setStylesheet(theme.getStylesheet());
|
||||||
|
if(savedTheme.getCreatedAt() == null) {
|
||||||
|
savedTheme.setCreatedAt(Instant.now());
|
||||||
|
}
|
||||||
|
mongockTemplate.save(savedTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(theme.getName().equalsIgnoreCase(Theme.LEGACY_THEME_NAME)) {
|
||||||
|
legacyTheme = savedTheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!themeExists) { // this is the first time we're running the migration
|
||||||
|
// migrate all applications and set legacy theme to them in both mode
|
||||||
|
Update update = new Update().set(fieldName(QApplication.application.publishedModeThemeId), legacyTheme.getId())
|
||||||
|
.set(fieldName(QApplication.application.editModeThemeId), legacyTheme.getId());
|
||||||
|
mongockTemplate.updateMulti(
|
||||||
|
new Query(where(fieldName(QApplication.application.deleted)).is(false)), update, Application.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,13 +58,13 @@ public class CustomCommentThreadRepositoryCEImpl extends BaseAppsmithRepositoryI
|
||||||
where(fieldName(QCommentThread.commentThread.applicationId)).is(applicationId),
|
where(fieldName(QCommentThread.commentThread.applicationId)).is(applicationId),
|
||||||
where(fieldName(QCommentThread.commentThread.isPrivate)).is(TRUE)
|
where(fieldName(QCommentThread.commentThread.isPrivate)).is(TRUE)
|
||||||
);
|
);
|
||||||
return queryOne(criteria, AclPermission.READ_THREAD);
|
return queryOne(criteria, AclPermission.READ_THREADS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<UpdateResult> removeSubscriber(String threadId, String username) {
|
public Mono<UpdateResult> removeSubscriber(String threadId, String username) {
|
||||||
Update update = new Update().pull(fieldName(QCommentThread.commentThread.subscribers), username);
|
Update update = new Update().pull(fieldName(QCommentThread.commentThread.subscribers), username);
|
||||||
return this.updateById(threadId, update, AclPermission.READ_THREAD);
|
return this.updateById(threadId, update, AclPermission.READ_THREADS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -92,7 +92,7 @@ public class CustomCommentThreadRepositoryCEImpl extends BaseAppsmithRepositoryI
|
||||||
where(fieldName(QCommentThread.commentThread.applicationId)).is(applicationId),
|
where(fieldName(QCommentThread.commentThread.applicationId)).is(applicationId),
|
||||||
where(resolvedActiveFieldKey).is(false)
|
where(resolvedActiveFieldKey).is(false)
|
||||||
);
|
);
|
||||||
return count(criteriaList, AclPermission.READ_THREAD);
|
return count(criteriaList, AclPermission.READ_THREADS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package com.appsmith.server.repositories.ce;
|
package com.appsmith.server.repositories.ce;
|
||||||
|
|
||||||
|
import com.appsmith.server.acl.AclPermission;
|
||||||
import com.appsmith.server.domains.Theme;
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.repositories.AppsmithRepository;
|
import com.appsmith.server.repositories.AppsmithRepository;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
public interface CustomThemeRepositoryCE extends AppsmithRepository<Theme> {
|
public interface CustomThemeRepositoryCE extends AppsmithRepository<Theme> {
|
||||||
|
Flux<Theme> getApplicationThemes(String applicationId, AclPermission aclPermission);
|
||||||
Flux<Theme> getSystemThemes();
|
Flux<Theme> getSystemThemes();
|
||||||
Mono<Theme> getSystemThemeByName(String themeName);
|
Mono<Theme> getSystemThemeByName(String themeName);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.appsmith.server.repositories.ce;
|
package com.appsmith.server.repositories.ce;
|
||||||
|
|
||||||
|
import com.appsmith.server.acl.AclPermission;
|
||||||
import com.appsmith.server.domains.QTheme;
|
import com.appsmith.server.domains.QTheme;
|
||||||
import com.appsmith.server.domains.Theme;
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
||||||
|
|
@ -24,10 +25,18 @@ public class CustomThemeRepositoryCEImpl extends BaseAppsmithRepositoryImpl<Them
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<Theme> getApplicationThemes(String applicationId, AclPermission aclPermission) {
|
||||||
|
Criteria appThemeCriteria = Criteria.where(fieldName(QTheme.theme.applicationId)).is(applicationId);
|
||||||
|
Criteria systemThemeCriteria = Criteria.where(fieldName(QTheme.theme.isSystemTheme)).is(Boolean.TRUE);
|
||||||
|
Criteria criteria = new Criteria().orOperator(appThemeCriteria, systemThemeCriteria);
|
||||||
|
return queryAll(List.of(criteria), aclPermission);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<Theme> getSystemThemes() {
|
public Flux<Theme> getSystemThemes() {
|
||||||
Criteria criteria = Criteria.where(fieldName(QTheme.theme.isSystemTheme)).is(Boolean.TRUE);
|
Criteria systemThemeCriteria = Criteria.where(fieldName(QTheme.theme.isSystemTheme)).is(Boolean.TRUE);
|
||||||
return queryAll(List.of(criteria), null);
|
return queryAll(List.of(systemThemeCriteria), AclPermission.READ_THEMES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -35,6 +44,6 @@ public class CustomThemeRepositoryCEImpl extends BaseAppsmithRepositoryImpl<Them
|
||||||
String findNameRegex = String.format("^%s$", Pattern.quote(themeName));
|
String findNameRegex = String.format("^%s$", Pattern.quote(themeName));
|
||||||
Criteria criteria = where(fieldName(QTheme.theme.name)).regex(findNameRegex, "i")
|
Criteria criteria = where(fieldName(QTheme.theme.name)).regex(findNameRegex, "i")
|
||||||
.and(fieldName(QTheme.theme.isSystemTheme)).is(true);
|
.and(fieldName(QTheme.theme.isSystemTheme)).is(true);
|
||||||
return queryOne(List.of(criteria), null);
|
return queryOne(List.of(criteria), AclPermission.READ_THEMES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.appsmith.server.services;
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
|
import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.repositories.ApplicationRepository;
|
import com.appsmith.server.repositories.ApplicationRepository;
|
||||||
import com.appsmith.server.repositories.ThemeRepository;
|
import com.appsmith.server.repositories.ThemeRepository;
|
||||||
import com.appsmith.server.services.ce.ThemeServiceCEImpl;
|
import com.appsmith.server.services.ce.ThemeServiceCEImpl;
|
||||||
|
|
@ -14,7 +15,7 @@ import javax.validation.Validator;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class ThemeServiceImpl extends ThemeServiceCEImpl implements ThemeService {
|
public class ThemeServiceImpl extends ThemeServiceCEImpl implements ThemeService {
|
||||||
public ThemeServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ThemeRepository repository, AnalyticsService analyticsService, ApplicationRepository applicationRepository) {
|
public ThemeServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ThemeRepository repository, AnalyticsService analyticsService, ApplicationRepository applicationRepository, PolicyGenerator policyGenerator) {
|
||||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService, applicationRepository);
|
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService, applicationRepository, policyGenerator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -697,16 +697,6 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
||||||
application1.setModifiedBy(applicationUserTuple2.getT2().getUsername()); // setting modified by to current user
|
application1.setModifiedBy(applicationUserTuple2.getT2().getUsername()); // setting modified by to current user
|
||||||
return applicationService.createDefault(application1);
|
return applicationService.createDefault(application1);
|
||||||
})
|
})
|
||||||
// duplicate the source application's themes if required i.e. if they were customized
|
|
||||||
.flatMap(application ->
|
|
||||||
themeService.cloneThemeToApplication(sourceApplication.getEditModeThemeId(), application.getId())
|
|
||||||
.zipWith(themeService.cloneThemeToApplication(sourceApplication.getPublishedModeThemeId(), application.getId()))
|
|
||||||
.map(themesZip -> {
|
|
||||||
application.setEditModeThemeId(themesZip.getT1().getId());
|
|
||||||
application.setPublishedModeThemeId(themesZip.getT2().getId());
|
|
||||||
return application;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
// Now fetch the pages of the source application, clone and add them to this new application
|
// Now fetch the pages of the source application, clone and add them to this new application
|
||||||
.flatMap(savedApplication -> Flux.fromIterable(sourceApplication.getPages())
|
.flatMap(savedApplication -> Flux.fromIterable(sourceApplication.getPages())
|
||||||
.flatMap(applicationPage -> {
|
.flatMap(applicationPage -> {
|
||||||
|
|
@ -728,6 +718,20 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
||||||
savedApplication.setPages(clonedPages);
|
savedApplication.setPages(clonedPages);
|
||||||
return applicationService.save(savedApplication);
|
return applicationService.save(savedApplication);
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
// duplicate the source application's themes if required i.e. if they were customized
|
||||||
|
.flatMap(application ->
|
||||||
|
themeService.cloneThemeToApplication(sourceApplication.getEditModeThemeId(), application)
|
||||||
|
.zipWith(themeService.cloneThemeToApplication(sourceApplication.getPublishedModeThemeId(), application))
|
||||||
|
.flatMap(themesZip -> {
|
||||||
|
String editModeThemeId = themesZip.getT1().getId();
|
||||||
|
String publishedModeThemeId = themesZip.getT2().getId();
|
||||||
|
application.setEditModeThemeId(editModeThemeId);
|
||||||
|
application.setPublishedModeThemeId(publishedModeThemeId);
|
||||||
|
return applicationService.setAppTheme(
|
||||||
|
application.getId(), editModeThemeId, publishedModeThemeId, MANAGE_APPLICATIONS
|
||||||
|
).thenReturn(application);
|
||||||
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -842,9 +846,9 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
||||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, applicationId)))
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, applicationId)))
|
||||||
.cache();
|
.cache();
|
||||||
|
|
||||||
Mono<Theme> publishThemeMono = applicationMono.flatMap(application -> themeService.publishTheme(
|
Mono<Theme> publishThemeMono = applicationMono.flatMap(
|
||||||
application.getEditModeThemeId(), application.getPublishedModeThemeId(), application.getId()
|
application -> themeService.publishTheme(application.getId())
|
||||||
));
|
);
|
||||||
|
|
||||||
Flux<NewPage> publishApplicationAndPages = applicationMono
|
Flux<NewPage> publishApplicationAndPages = applicationMono
|
||||||
//Return all the pages in the Application
|
//Return all the pages in the Application
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.GitAuth;
|
import com.appsmith.server.domains.GitAuth;
|
||||||
import com.appsmith.server.dtos.ApplicationAccessDTO;
|
import com.appsmith.server.dtos.ApplicationAccessDTO;
|
||||||
import com.appsmith.server.services.CrudService;
|
import com.appsmith.server.services.CrudService;
|
||||||
|
import com.mongodb.client.result.UpdateResult;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
|
@ -62,4 +63,6 @@ public interface ApplicationServiceCE extends CrudService<Application, String> {
|
||||||
|
|
||||||
String getRandomAppCardColor();
|
String getRandomAppCardColor();
|
||||||
|
|
||||||
|
Mono<UpdateResult> setAppTheme(String applicationId, String editModeThemeId, String publishedModeThemeId, AclPermission aclPermission);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import com.appsmith.server.domains.GitAuth;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.domains.NewPage;
|
import com.appsmith.server.domains.NewPage;
|
||||||
import com.appsmith.server.domains.Page;
|
import com.appsmith.server.domains.Page;
|
||||||
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.dtos.ActionDTO;
|
import com.appsmith.server.dtos.ActionDTO;
|
||||||
import com.appsmith.server.dtos.ApplicationAccessDTO;
|
import com.appsmith.server.dtos.ApplicationAccessDTO;
|
||||||
|
|
@ -28,6 +29,7 @@ import com.appsmith.server.services.AnalyticsService;
|
||||||
import com.appsmith.server.services.BaseService;
|
import com.appsmith.server.services.BaseService;
|
||||||
import com.appsmith.server.services.ConfigService;
|
import com.appsmith.server.services.ConfigService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
|
import com.mongodb.client.result.UpdateResult;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
|
|
@ -308,18 +310,23 @@ public class ApplicationServiceCEImpl extends BaseService<ApplicationRepository,
|
||||||
Map<String, Policy> pagePolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(applicationPolicyMap, Application.class, Page.class);
|
Map<String, Policy> pagePolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(applicationPolicyMap, Application.class, Page.class);
|
||||||
Map<String, Policy> actionPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(pagePolicyMap, Page.class, Action.class);
|
Map<String, Policy> actionPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(pagePolicyMap, Page.class, Action.class);
|
||||||
Map<String, Policy> datasourcePolicyMap = policyUtils.generatePolicyFromPermission(Set.of(EXECUTE_DATASOURCES), user);
|
Map<String, Policy> datasourcePolicyMap = policyUtils.generatePolicyFromPermission(Set.of(EXECUTE_DATASOURCES), user);
|
||||||
|
Map<String, Policy> themePolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
||||||
|
applicationPolicyMap, Application.class, Theme.class
|
||||||
|
);
|
||||||
|
|
||||||
final Flux<NewPage> updatedPagesFlux = policyUtils
|
final Flux<NewPage> updatedPagesFlux = policyUtils
|
||||||
.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, isPublic);
|
.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, isPublic);
|
||||||
// Use the same policy map as actions for action collections since action collections have the same kind of permissions
|
// Use the same policy map as actions for action collections since action collections have the same kind of permissions
|
||||||
final Flux<ActionCollection> updatedActionCollectionsFlux = policyUtils
|
final Flux<ActionCollection> updatedActionCollectionsFlux = policyUtils
|
||||||
.updateWithPagePermissionsToAllItsActionCollections(application.getId(), actionPolicyMap, isPublic);
|
.updateWithPagePermissionsToAllItsActionCollections(application.getId(), actionPolicyMap, isPublic);
|
||||||
|
Flux<Theme> updatedThemesFlux = policyUtils.updateThemePolicies(application, themePolicyMap, isPublic);
|
||||||
final Flux<NewAction> updatedActionsFlux = updatedPagesFlux
|
final Flux<NewAction> updatedActionsFlux = updatedPagesFlux
|
||||||
.collectList()
|
.collectList()
|
||||||
.thenMany(updatedActionCollectionsFlux)
|
.thenMany(updatedActionCollectionsFlux)
|
||||||
.collectList()
|
.collectList()
|
||||||
.then(Mono.justOrEmpty(application.getId()))
|
.then(Mono.justOrEmpty(application.getId()))
|
||||||
|
.thenMany(updatedThemesFlux)
|
||||||
|
.collectList()
|
||||||
.flatMapMany(applicationId -> policyUtils.updateWithPagePermissionsToAllItsActions(application.getId(), actionPolicyMap, isPublic));
|
.flatMapMany(applicationId -> policyUtils.updateWithPagePermissionsToAllItsActions(application.getId(), actionPolicyMap, isPublic));
|
||||||
|
|
||||||
return updatedActionsFlux
|
return updatedActionsFlux
|
||||||
|
|
@ -547,4 +554,8 @@ public class ApplicationServiceCEImpl extends BaseService<ApplicationRepository,
|
||||||
return ApplicationConstants.APP_CARD_COLORS[randomColorIndex];
|
return ApplicationConstants.APP_CARD_COLORS[randomColorIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<UpdateResult> setAppTheme(String applicationId, String editModeThemeId, String publishedModeThemeId, AclPermission aclPermission) {
|
||||||
|
return repository.setAppTheme(applicationId, editModeThemeId, publishedModeThemeId, aclPermission);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,9 @@ import static com.appsmith.server.acl.AclPermission.COMMENT_ON_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_COMMENT;
|
import static com.appsmith.server.acl.AclPermission.READ_COMMENTS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_THREAD;
|
import static com.appsmith.server.acl.AclPermission.READ_THREADS;
|
||||||
import static com.appsmith.server.constants.CommentConstants.APPSMITH_BOT_NAME;
|
import static com.appsmith.server.constants.CommentConstants.APPSMITH_BOT_NAME;
|
||||||
import static com.appsmith.server.constants.CommentConstants.APPSMITH_BOT_USERNAME;
|
import static com.appsmith.server.constants.CommentConstants.APPSMITH_BOT_USERNAME;
|
||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
|
|
@ -177,7 +177,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
comment.setPageId(branchedPageId);
|
comment.setPageId(branchedPageId);
|
||||||
comment.setApplicationId(branchedApplicationId);
|
comment.setApplicationId(branchedApplicationId);
|
||||||
return threadRepository
|
return threadRepository
|
||||||
.findById(threadId, AclPermission.COMMENT_ON_THREAD)
|
.findById(threadId, AclPermission.COMMENT_ON_THREADS)
|
||||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.COMMENT_THREAD, threadId)))
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.COMMENT_THREAD, threadId)))
|
||||||
.flatMap(commentThread -> updateThreadOnAddComment(commentThread, comment, user))
|
.flatMap(commentThread -> updateThreadOnAddComment(commentThread, comment, user))
|
||||||
.flatMap(commentThread -> create(commentThread, user, comment, originHeader));
|
.flatMap(commentThread -> create(commentThread, user, comment, originHeader));
|
||||||
|
|
@ -188,7 +188,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
@Override
|
@Override
|
||||||
public Mono<Comment> findByIdAndBranchName(String id, String branchName) {
|
public Mono<Comment> findByIdAndBranchName(String id, String branchName) {
|
||||||
// Ignore branch name as comments are not shared across git branches
|
// Ignore branch name as comments are not shared across git branches
|
||||||
return repository.findById(id, READ_COMMENT)
|
return repository.findById(id, READ_COMMENTS)
|
||||||
.map(responseUtils::updatePageAndAppIdWithDefaultResourcesForComments);
|
.map(responseUtils::updatePageAndAppIdWithDefaultResourcesForComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,9 +234,9 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
CommentThread.class
|
CommentThread.class
|
||||||
));
|
));
|
||||||
policies.add(policyUtils.generatePolicyFromPermission(
|
policies.add(policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_THREAD),
|
Set.of(AclPermission.MANAGE_THREADS),
|
||||||
commentThread.getAuthorUsername()
|
commentThread.getAuthorUsername()
|
||||||
).get(AclPermission.MANAGE_THREAD.getValue()));
|
).get(AclPermission.MANAGE_THREADS.getValue()));
|
||||||
commentThread.setPolicies(policies);
|
commentThread.setPolicies(policies);
|
||||||
return commentThread;
|
return commentThread;
|
||||||
});
|
});
|
||||||
|
|
@ -266,9 +266,9 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
Comment.class
|
Comment.class
|
||||||
);
|
);
|
||||||
policies.add(policyUtils.generatePolicyFromPermission(
|
policies.add(policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_COMMENT),
|
Set.of(AclPermission.MANAGE_COMMENTS),
|
||||||
user
|
user
|
||||||
).get(AclPermission.MANAGE_COMMENT.getValue()));
|
).get(AclPermission.MANAGE_COMMENTS.getValue()));
|
||||||
comment.setPolicies(policies);
|
comment.setPolicies(policies);
|
||||||
|
|
||||||
Mono<Comment> commentMono;
|
Mono<Comment> commentMono;
|
||||||
|
|
@ -429,7 +429,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
// Comments and threads can't be moved between the pages and applications.
|
// Comments and threads can't be moved between the pages and applications.
|
||||||
comment.setApplicationId(null);
|
comment.setApplicationId(null);
|
||||||
comment.setPageId(null);
|
comment.setPageId(null);
|
||||||
return repository.updateById(id, comment, AclPermission.MANAGE_COMMENT)
|
return repository.updateById(id, comment, AclPermission.MANAGE_COMMENTS)
|
||||||
.flatMap(analyticsService::sendUpdateEvent)
|
.flatMap(analyticsService::sendUpdateEvent)
|
||||||
.map(responseUtils::updatePageAndAppIdWithDefaultResourcesForComments);
|
.map(responseUtils::updatePageAndAppIdWithDefaultResourcesForComments);
|
||||||
}
|
}
|
||||||
|
|
@ -447,7 +447,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
} else {
|
} else {
|
||||||
return Mono.just(user);
|
return Mono.just(user);
|
||||||
}
|
}
|
||||||
}).zipWith(threadRepository.findById(threadId, AclPermission.READ_THREAD))
|
}).zipWith(threadRepository.findById(threadId, AclPermission.READ_THREADS))
|
||||||
.switchIfEmpty(Mono.error(
|
.switchIfEmpty(Mono.error(
|
||||||
new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, "comment thread", threadId))
|
new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, "comment thread", threadId))
|
||||||
)
|
)
|
||||||
|
|
@ -489,7 +489,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
}
|
}
|
||||||
|
|
||||||
return threadRepository
|
return threadRepository
|
||||||
.updateById(threadId, commentThread, AclPermission.READ_THREAD)
|
.updateById(threadId, commentThread, AclPermission.READ_THREADS)
|
||||||
.flatMap(updatedThread -> {
|
.flatMap(updatedThread -> {
|
||||||
updatedThread.setIsViewed(true);
|
updatedThread.setIsViewed(true);
|
||||||
// Update branched applicationId and pageId with default Ids
|
// Update branched applicationId and pageId with default Ids
|
||||||
|
|
@ -520,7 +520,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
public Flux<Comment> get(MultiValueMap<String, String> params) {
|
public Flux<Comment> get(MultiValueMap<String, String> params) {
|
||||||
// Remove branch name as comments are not shared across branches
|
// Remove branch name as comments are not shared across branches
|
||||||
params.remove(FieldName.DEFAULT_RESOURCES + "." + FieldName.BRANCH_NAME);
|
params.remove(FieldName.DEFAULT_RESOURCES + "." + FieldName.BRANCH_NAME);
|
||||||
return super.getWithPermission(params, READ_COMMENT)
|
return super.getWithPermission(params, READ_COMMENTS)
|
||||||
.map(responseUtils::updatePageAndAppIdWithDefaultResourcesForComments);
|
.map(responseUtils::updatePageAndAppIdWithDefaultResourcesForComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -580,7 +580,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
// user is app viewer, show only PUBLISHED comment threads
|
// user is app viewer, show only PUBLISHED comment threads
|
||||||
commentThreadFilterDTO.setMode(ApplicationMode.PUBLISHED);
|
commentThreadFilterDTO.setMode(ApplicationMode.PUBLISHED);
|
||||||
}
|
}
|
||||||
return threadRepository.find(commentThreadFilterDTO, AclPermission.READ_THREAD)
|
return threadRepository.find(commentThreadFilterDTO, AclPermission.READ_THREADS)
|
||||||
.collectList()
|
.collectList()
|
||||||
.flatMap(threads -> {
|
.flatMap(threads -> {
|
||||||
final Map<String, CommentThread> threadsByThreadId = new HashMap<>();
|
final Map<String, CommentThread> threadsByThreadId = new HashMap<>();
|
||||||
|
|
@ -626,10 +626,10 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Mono<Comment> deleteComment(String id) {
|
public Mono<Comment> deleteComment(String id) {
|
||||||
return repository.findById(id, AclPermission.MANAGE_COMMENT)
|
return repository.findById(id, AclPermission.MANAGE_COMMENTS)
|
||||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.COMMENT, id)))
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.COMMENT, id)))
|
||||||
.flatMap(repository::archive)
|
.flatMap(repository::archive)
|
||||||
.flatMap(comment -> threadRepository.findById(comment.getThreadId(), READ_THREAD).flatMap(commentThread ->
|
.flatMap(comment -> threadRepository.findById(comment.getThreadId(), READ_THREADS).flatMap(commentThread ->
|
||||||
sendCommentNotifications(commentThread.getSubscribers(), comment, CommentNotificationEvent.DELETED)
|
sendCommentNotifications(commentThread.getSubscribers(), comment, CommentNotificationEvent.DELETED)
|
||||||
.thenReturn(comment)
|
.thenReturn(comment)
|
||||||
))
|
))
|
||||||
|
|
@ -639,7 +639,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<CommentThread> deleteThread(String threadId) {
|
public Mono<CommentThread> deleteThread(String threadId) {
|
||||||
return threadRepository.findById(threadId, AclPermission.MANAGE_THREAD)
|
return threadRepository.findById(threadId, AclPermission.MANAGE_THREADS)
|
||||||
.flatMap(threadRepository::archive)
|
.flatMap(threadRepository::archive)
|
||||||
.flatMap(commentThread ->
|
.flatMap(commentThread ->
|
||||||
notificationService.createNotification(
|
notificationService.createNotification(
|
||||||
|
|
@ -653,7 +653,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
@Override
|
@Override
|
||||||
public Mono<Boolean> createReaction(String commentId, Comment.Reaction reaction) {
|
public Mono<Boolean> createReaction(String commentId, Comment.Reaction reaction) {
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
repository.findById(commentId, READ_COMMENT),
|
repository.findById(commentId, READ_COMMENTS),
|
||||||
sessionUserService.getCurrentUser()
|
sessionUserService.getCurrentUser()
|
||||||
)
|
)
|
||||||
.flatMap(tuple -> {
|
.flatMap(tuple -> {
|
||||||
|
|
@ -671,7 +671,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
@Override
|
@Override
|
||||||
public Mono<Boolean> deleteReaction(String commentId, Comment.Reaction reaction) {
|
public Mono<Boolean> deleteReaction(String commentId, Comment.Reaction reaction) {
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
repository.findById(commentId, READ_COMMENT),
|
repository.findById(commentId, READ_COMMENTS),
|
||||||
sessionUserService.getCurrentUser()
|
sessionUserService.getCurrentUser()
|
||||||
)
|
)
|
||||||
.flatMap(tuple -> {
|
.flatMap(tuple -> {
|
||||||
|
|
@ -702,7 +702,7 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
Mono<Long> commentSeq;
|
Mono<Long> commentSeq;
|
||||||
if (TRUE.equals(commentThread.getIsPrivate())) {
|
if (TRUE.equals(commentThread.getIsPrivate())) {
|
||||||
Collection<Policy> policyCollection = policyUtils.generatePolicyFromPermission(
|
Collection<Policy> policyCollection = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_THREAD, AclPermission.COMMENT_ON_THREAD),
|
Set.of(AclPermission.MANAGE_THREADS, AclPermission.COMMENT_ON_THREADS),
|
||||||
user
|
user
|
||||||
).values();
|
).values();
|
||||||
policies.addAll(policyCollection);
|
policies.addAll(policyCollection);
|
||||||
|
|
@ -714,9 +714,9 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
CommentThread.class
|
CommentThread.class
|
||||||
));
|
));
|
||||||
policies.add(policyUtils.generatePolicyFromPermission(
|
policies.add(policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_THREAD),
|
Set.of(AclPermission.MANAGE_THREADS),
|
||||||
user
|
user
|
||||||
).get(AclPermission.MANAGE_THREAD.getValue()));
|
).get(AclPermission.MANAGE_THREADS.getValue()));
|
||||||
commentSeq = sequenceService.getNext(CommentThread.class, application.getId());
|
commentSeq = sequenceService.getNext(CommentThread.class, application.getId());
|
||||||
}
|
}
|
||||||
commentThread.setPolicies(policies);
|
commentThread.setPolicies(policies);
|
||||||
|
|
@ -740,9 +740,9 @@ public class CommentServiceCEImpl extends BaseService<CommentRepository, Comment
|
||||||
Comment.class
|
Comment.class
|
||||||
);
|
);
|
||||||
policies.add(policyUtils.generatePolicyFromPermission(
|
policies.add(policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_COMMENT),
|
Set.of(AclPermission.MANAGE_COMMENTS),
|
||||||
user
|
user
|
||||||
).get(AclPermission.MANAGE_COMMENT.getValue()));
|
).get(AclPermission.MANAGE_COMMENTS.getValue()));
|
||||||
comment.setPolicies(policies);
|
comment.setPolicies(policies);
|
||||||
|
|
||||||
Comment.Block block = new Comment.Block();
|
Comment.Block block = new Comment.Block();
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
package com.appsmith.server.services.ce;
|
package com.appsmith.server.services.ce;
|
||||||
|
|
||||||
|
import com.appsmith.server.acl.AclPermission;
|
||||||
|
import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.ApplicationMode;
|
import com.appsmith.server.domains.ApplicationMode;
|
||||||
import com.appsmith.server.domains.Theme;
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.services.CrudService;
|
import com.appsmith.server.services.CrudService;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
public interface ThemeServiceCE extends CrudService<Theme, String> {
|
public interface ThemeServiceCE extends CrudService<Theme, String> {
|
||||||
Mono<Theme> getApplicationTheme(String applicationId, ApplicationMode applicationMode);
|
Mono<Theme> getApplicationTheme(String applicationId, ApplicationMode applicationMode);
|
||||||
|
Flux<Theme> getApplicationThemes(String applicationId);
|
||||||
|
Flux<Theme> getSystemThemes();
|
||||||
|
Mono<Theme> getSystemTheme(String themeName);
|
||||||
Mono<Theme> updateTheme(String applicationId, Theme resource);
|
Mono<Theme> updateTheme(String applicationId, Theme resource);
|
||||||
Mono<Theme> changeCurrentTheme(String themeId, String applicationId);
|
Mono<Theme> changeCurrentTheme(String themeId, String applicationId);
|
||||||
|
|
||||||
|
|
@ -18,14 +24,17 @@ public interface ThemeServiceCE extends CrudService<Theme, String> {
|
||||||
Mono<String> getDefaultThemeId();
|
Mono<String> getDefaultThemeId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duplicates a theme if the theme is customized one. It'll set the application id to the new theme.
|
* Duplicates a theme if the theme is customized one.
|
||||||
* If the source theme is a system theme, it'll skip creating a new theme and return the system theme instead.
|
* If the source theme is a system theme, it'll skip creating a new theme and return the system theme instead.
|
||||||
* @param srcThemeId ID of source theme that needs to be duplicated
|
* @param srcThemeId ID of source theme that needs to be duplicated
|
||||||
* @param destApplicationId ID of the application for which theme'll be created
|
* @param destApplicationId ID of the application for which theme'll be created
|
||||||
* @return newly created theme if source is not system theme, otherwise return the system theme
|
* @return newly created theme if source is not system theme, otherwise return the system theme
|
||||||
*/
|
*/
|
||||||
Mono<Theme> cloneThemeToApplication(String srcThemeId, String destApplicationId);
|
Mono<Theme> cloneThemeToApplication(String srcThemeId, Application destApplication);
|
||||||
|
Mono<Theme> publishTheme(String applicationId);
|
||||||
Mono<Theme> publishTheme(String editModeThemeId, String publishedThemeId, String applicationId);
|
Mono<Theme> persistCurrentTheme(String applicationId, Theme theme);
|
||||||
void resetDefaultThemeIdCache();
|
Mono<Theme> getThemeById(String themeId, AclPermission permission);
|
||||||
|
Mono<Theme> save(Theme theme);
|
||||||
|
Mono<Theme> updateName(String id, Theme theme);
|
||||||
|
Mono<Theme> getOrSaveTheme(Theme theme, Application destApplication);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.appsmith.server.services.ce;
|
package com.appsmith.server.services.ce;
|
||||||
|
|
||||||
import com.appsmith.server.acl.AclPermission;
|
import com.appsmith.server.acl.AclPermission;
|
||||||
|
import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
|
import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.ApplicationMode;
|
import com.appsmith.server.domains.ApplicationMode;
|
||||||
import com.appsmith.server.domains.Theme;
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
|
@ -19,43 +21,48 @@ import org.springframework.util.StringUtils;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
|
import reactor.util.function.Tuples;
|
||||||
|
|
||||||
import javax.validation.Validator;
|
import javax.validation.Validator;
|
||||||
|
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.MANAGE_THEMES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ThemeServiceCEImpl extends BaseService<ThemeRepositoryCE, Theme, String> implements ThemeServiceCE {
|
public class ThemeServiceCEImpl extends BaseService<ThemeRepositoryCE, Theme, String> implements ThemeServiceCE {
|
||||||
|
|
||||||
private final ApplicationRepository applicationRepository;
|
private final ApplicationRepository applicationRepository;
|
||||||
|
private final PolicyGenerator policyGenerator;
|
||||||
private String defaultThemeId; // acts as a simple cache so that we don't need to fetch from DB always
|
private String defaultThemeId; // acts as a simple cache so that we don't need to fetch from DB always
|
||||||
|
|
||||||
public ThemeServiceCEImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ThemeRepository repository, AnalyticsService analyticsService, ApplicationRepository applicationRepository) {
|
public ThemeServiceCEImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ThemeRepository repository, AnalyticsService analyticsService, ApplicationRepository applicationRepository, PolicyGenerator policyGenerator) {
|
||||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
||||||
this.applicationRepository = applicationRepository;
|
this.applicationRepository = applicationRepository;
|
||||||
}
|
this.policyGenerator = policyGenerator;
|
||||||
|
|
||||||
@Override
|
|
||||||
public Flux<Theme> get(MultiValueMap<String, String> params) {
|
|
||||||
return repository.getSystemThemes(); // return the list of system themes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Theme> create(Theme resource) {
|
public Mono<Theme> create(Theme resource) {
|
||||||
// user can get the list of themes under an application only
|
return repository.save(resource);
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Theme> update(String s, Theme resource) {
|
public Mono<Theme> update(String s, Theme resource) {
|
||||||
// we don't allow to update a theme by id, user can only update a theme under their application
|
// we don't allow to update a theme by id, user can only update a theme under their application
|
||||||
throw new UnsupportedOperationException();
|
throw new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Theme> getById(String s) {
|
public Mono<Theme> getById(String s) {
|
||||||
// TODO: better to add permission check
|
// we don't allow to get a theme by id from DB
|
||||||
return repository.findById(s);
|
throw new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<Theme> get(MultiValueMap<String, String> params) {
|
||||||
|
// we return all system themes
|
||||||
|
return repository.getSystemThemes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -69,45 +76,75 @@ public class ThemeServiceCEImpl extends BaseService<ThemeRepositoryCE, Theme, St
|
||||||
if(applicationMode == ApplicationMode.PUBLISHED) {
|
if(applicationMode == ApplicationMode.PUBLISHED) {
|
||||||
themeId = application.getPublishedModeThemeId();
|
themeId = application.getPublishedModeThemeId();
|
||||||
}
|
}
|
||||||
if(!StringUtils.isEmpty(themeId)) {
|
if(StringUtils.hasLength(themeId)) {
|
||||||
return repository.findById(themeId);
|
return repository.findById(themeId, READ_THEMES);
|
||||||
} else { // theme id is not present, return default theme
|
} else { // theme id is not present, return default theme
|
||||||
return repository.getSystemThemeByName(Theme.DEFAULT_THEME_NAME);
|
return repository.getSystemThemeByName(Theme.DEFAULT_THEME_NAME);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<Theme> getApplicationThemes(String applicationId) {
|
||||||
|
return repository.getApplicationThemes(applicationId, READ_THEMES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<Theme> getSystemThemes() {
|
||||||
|
return repository.getSystemThemes();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Theme> updateTheme(String applicationId, Theme resource) {
|
public Mono<Theme> updateTheme(String applicationId, Theme resource) {
|
||||||
return applicationRepository.findById(applicationId, AclPermission.MANAGE_APPLICATIONS)
|
return applicationRepository.findById(applicationId, AclPermission.MANAGE_APPLICATIONS)
|
||||||
.flatMap(application -> {
|
.flatMap(application -> {
|
||||||
// makes sure user has permission to edit application and an application exists by this applicationId
|
// makes sure user has permission to edit application and an application exists by this applicationId
|
||||||
// check if this application has already a customized them
|
// check if this application has already a customized them
|
||||||
return saveThemeForApplication(application.getEditModeThemeId(), resource, applicationId, ApplicationMode.EDIT);
|
return saveThemeForApplication(application.getEditModeThemeId(), resource, application, ApplicationMode.EDIT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Theme> changeCurrentTheme(String newThemeId, String applicationId) {
|
public Mono<Theme> changeCurrentTheme(String newThemeId, String applicationId) {
|
||||||
// set provided theme to application and return that theme object
|
|
||||||
Mono<Theme> setAppThemeMono = applicationRepository.setAppTheme(
|
|
||||||
applicationId, newThemeId,null, MANAGE_APPLICATIONS
|
|
||||||
).then(repository.findById(newThemeId));
|
|
||||||
|
|
||||||
// in case a customized theme was set to application, we need to delete that
|
|
||||||
return applicationRepository.findById(applicationId, AclPermission.MANAGE_APPLICATIONS)
|
return applicationRepository.findById(applicationId, AclPermission.MANAGE_APPLICATIONS)
|
||||||
.switchIfEmpty(Mono.error(
|
.switchIfEmpty(Mono.error(
|
||||||
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, applicationId))
|
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, applicationId))
|
||||||
)
|
)
|
||||||
.flatMap(application -> repository.findById(application.getEditModeThemeId())
|
.flatMap(application -> repository.findById(application.getEditModeThemeId(), READ_THEMES)
|
||||||
.defaultIfEmpty(new Theme())
|
.defaultIfEmpty(new Theme())
|
||||||
.flatMap(currentTheme -> {
|
.zipWith(repository.findById(newThemeId, READ_THEMES))
|
||||||
if (!StringUtils.isEmpty(currentTheme.getId()) && !currentTheme.isSystemTheme()) {
|
.flatMap(themeTuple2 -> {
|
||||||
// current theme is not a system theme but customized one, delete this
|
Theme currentTheme = themeTuple2.getT1();
|
||||||
return repository.delete(currentTheme).then(setAppThemeMono);
|
Theme newTheme = themeTuple2.getT2();
|
||||||
|
Mono<Theme> saveThemeMono;
|
||||||
|
if(!newTheme.isSystemTheme()) {
|
||||||
|
// we'll create a copy of newTheme
|
||||||
|
newTheme.setId(null);
|
||||||
|
newTheme.setApplicationId(null);
|
||||||
|
newTheme.setOrganizationId(null);
|
||||||
|
newTheme.setPolicies(policyGenerator.getAllChildPolicies(
|
||||||
|
application.getPolicies(), Application.class, Theme.class
|
||||||
|
));
|
||||||
|
saveThemeMono = repository.save(newTheme);
|
||||||
|
} else {
|
||||||
|
saveThemeMono = Mono.just(newTheme);
|
||||||
}
|
}
|
||||||
return setAppThemeMono;
|
|
||||||
}));
|
return saveThemeMono.flatMap(savedTheme -> {
|
||||||
|
if (StringUtils.hasLength(currentTheme.getId()) && !currentTheme.isSystemTheme()
|
||||||
|
&& !StringUtils.hasLength(currentTheme.getApplicationId())) {
|
||||||
|
// current theme is neither a system theme nor app theme, delete the user customizations
|
||||||
|
return repository.delete(currentTheme).then(applicationRepository.setAppTheme(
|
||||||
|
applicationId, savedTheme.getId(),null, MANAGE_APPLICATIONS
|
||||||
|
)).thenReturn(savedTheme);
|
||||||
|
} else {
|
||||||
|
return applicationRepository.setAppTheme(
|
||||||
|
applicationId, savedTheme.getId(),null, MANAGE_APPLICATIONS
|
||||||
|
).thenReturn(savedTheme);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -122,79 +159,97 @@ public class ThemeServiceCEImpl extends BaseService<ThemeRepositoryCE, Theme, St
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Theme> cloneThemeToApplication(String srcThemeId, String destApplicationId) {
|
public Mono<Theme> cloneThemeToApplication(String srcThemeId, Application destApplication) {
|
||||||
return applicationRepository.findById(destApplicationId, MANAGE_APPLICATIONS).then(
|
return repository.findById(srcThemeId, READ_THEMES).flatMap(theme -> {
|
||||||
// make sure the current user has permission to manage application
|
if (theme.isSystemTheme()) { // it's a system theme, no need to copy
|
||||||
repository.findById(srcThemeId).flatMap(theme -> {
|
return Mono.just(theme);
|
||||||
if (theme.isSystemTheme()) { // it's a system theme, no need to copy
|
} else { // it's a customized theme, create a copy and return the copy
|
||||||
return Mono.just(theme);
|
theme.setId(null); // setting id to null so that save method will create a new instance
|
||||||
} else { // it's a customized theme, create a copy and return the copy
|
theme.setApplicationId(null);
|
||||||
theme.setId(null); // setting id to null so that save method will create a new instance
|
theme.setOrganizationId(null);
|
||||||
return repository.save(theme);
|
theme.setPolicies(policyGenerator.getAllChildPolicies(
|
||||||
}
|
destApplication.getPolicies(), Application.class, Theme.class
|
||||||
})
|
));
|
||||||
);
|
return repository.save(theme);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Theme> publishTheme(String editModeThemeId, String publishedThemeId, String applicationId) {
|
|
||||||
Mono<Theme> editModeThemeMono;
|
|
||||||
if(!StringUtils.hasLength(editModeThemeId)) { // theme id is empty, use the default theme
|
|
||||||
editModeThemeMono = repository.getSystemThemeByName(Theme.LEGACY_THEME_NAME);
|
|
||||||
} else { // theme id is not empty, fetch it by id
|
|
||||||
editModeThemeMono = repository.findById(editModeThemeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mono<Theme> publishThemeMono = editModeThemeMono.flatMap(editModeTheme -> {
|
|
||||||
if (editModeTheme.isSystemTheme()) { // system theme is set as edit mode theme
|
|
||||||
// just set the system theme id as edit and published mode theme id to application object
|
|
||||||
return applicationRepository.setAppTheme(
|
|
||||||
applicationId, editModeTheme.getId(), editModeTheme.getId(), MANAGE_APPLICATIONS
|
|
||||||
).thenReturn(editModeTheme);
|
|
||||||
} else { // a customized theme is set as edit mode theme, copy that theme for published mode
|
|
||||||
return saveThemeForApplication(publishedThemeId, editModeTheme, applicationId, ApplicationMode.PUBLISHED);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publishes a theme from edit mode to published mode
|
||||||
|
* @param applicationId application id
|
||||||
|
* @return Mono of theme object that was set in published mode
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<Theme> publishTheme(String applicationId) {
|
||||||
// fetch application to make sure user has permission to manage this application
|
// fetch application to make sure user has permission to manage this application
|
||||||
return applicationRepository.findById(applicationId, MANAGE_APPLICATIONS).then(publishThemeMono);
|
return applicationRepository.findById(applicationId, MANAGE_APPLICATIONS).flatMap(application -> {
|
||||||
|
Mono<Theme> editModeThemeMono;
|
||||||
|
if(!StringUtils.hasLength(application.getEditModeThemeId())) { // theme id is empty, use the default theme
|
||||||
|
editModeThemeMono = repository.getSystemThemeByName(Theme.LEGACY_THEME_NAME);
|
||||||
|
} else { // theme id is not empty, fetch it by id
|
||||||
|
editModeThemeMono = repository.findById(application.getEditModeThemeId(), READ_THEMES);
|
||||||
|
}
|
||||||
|
|
||||||
|
return editModeThemeMono.flatMap(editModeTheme -> {
|
||||||
|
if (editModeTheme.isSystemTheme()) { // system theme is set as edit mode theme
|
||||||
|
// Delete published mode theme if it was a copy of custom theme
|
||||||
|
return deletePublishedCustomizedThemeCopy(application.getPublishedModeThemeId()).then(
|
||||||
|
// Set the system theme id as edit and published mode theme id to application object
|
||||||
|
applicationRepository.setAppTheme(
|
||||||
|
applicationId, editModeTheme.getId(), editModeTheme.getId(), MANAGE_APPLICATIONS
|
||||||
|
)
|
||||||
|
).thenReturn(editModeTheme);
|
||||||
|
} else { // a customized theme is set as edit mode theme, copy that theme for published mode
|
||||||
|
return saveThemeForApplication(
|
||||||
|
application.getPublishedModeThemeId(), editModeTheme, application, ApplicationMode.PUBLISHED
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new theme if Theme with provided themeId is a system theme.
|
* Creates a new theme if Theme with provided themeId is a system theme.
|
||||||
* It sets the properties from the provided theme resource to the existing or newly created theme.
|
* It sets the properties from the provided theme resource to the existing or newly created theme.
|
||||||
* It'll also update the application if a new theme was created.
|
* It'll also update the application if a new theme was created.
|
||||||
* @param themeId ID of the existing theme that might be updated
|
* @param currentThemeId ID of the existing theme that might be updated
|
||||||
* @param resource new theme DTO that'll be stored as a new theme or override the existing theme
|
* @param targetThemeResource new theme DTO that'll be stored as a new theme or override the existing theme
|
||||||
* @param applicationId Application that contains the theme
|
* @param application Application that contains the theme
|
||||||
* @param applicationMode In which mode this theme will be set
|
* @param applicationMode In which mode this theme will be set
|
||||||
* @return Updated or newly created theme Publisher
|
* @return Updated or newly created theme Publisher
|
||||||
*/
|
*/
|
||||||
private Mono<Theme> saveThemeForApplication(String themeId, Theme resource, String applicationId, ApplicationMode applicationMode) {
|
private Mono<Theme> saveThemeForApplication(String currentThemeId, Theme targetThemeResource, Application application, ApplicationMode applicationMode) {
|
||||||
return repository.findById(themeId)
|
return repository.findById(currentThemeId, READ_THEMES)
|
||||||
.flatMap(theme -> {
|
.flatMap(currentTheme -> {
|
||||||
// set the edit mode values to published mode theme
|
// set the edit mode values to published mode theme
|
||||||
theme.setConfig(resource.getConfig());
|
currentTheme.setConfig(targetThemeResource.getConfig());
|
||||||
theme.setStylesheet(resource.getStylesheet());
|
currentTheme.setStylesheet(targetThemeResource.getStylesheet());
|
||||||
theme.setProperties(resource.getProperties());
|
currentTheme.setProperties(targetThemeResource.getProperties());
|
||||||
theme.setName(resource.getName());
|
if(StringUtils.hasLength(targetThemeResource.getName())) {
|
||||||
|
currentTheme.setName(targetThemeResource.getName());
|
||||||
|
}
|
||||||
boolean newThemeCreated = false;
|
boolean newThemeCreated = false;
|
||||||
if (theme.isSystemTheme()) {
|
if (currentTheme.isSystemTheme()) {
|
||||||
// if this is a system theme, create a new one
|
// if this is a system theme, create a new one
|
||||||
theme.setId(null); // setting id to null will create a new theme
|
currentTheme.setId(null); // setting id to null will create a new theme
|
||||||
theme.setSystemTheme(false);
|
currentTheme.setSystemTheme(false);
|
||||||
|
currentTheme.setPolicies(policyGenerator.getAllChildPolicies(
|
||||||
|
application.getPolicies(), Application.class, Theme.class
|
||||||
|
));
|
||||||
newThemeCreated = true;
|
newThemeCreated = true;
|
||||||
}
|
}
|
||||||
return repository.save(theme).zipWith(Mono.just(newThemeCreated));
|
return repository.save(currentTheme).zipWith(Mono.just(newThemeCreated));
|
||||||
}).flatMap(savedThemeTuple -> {
|
}).flatMap(savedThemeTuple -> {
|
||||||
Theme theme = savedThemeTuple.getT1();
|
Theme theme = savedThemeTuple.getT1();
|
||||||
if (savedThemeTuple.getT2()) { // new published theme created, update the application
|
if (savedThemeTuple.getT2()) { // new theme created, update the application
|
||||||
if(applicationMode == ApplicationMode.EDIT) {
|
if(applicationMode == ApplicationMode.EDIT) {
|
||||||
return applicationRepository.setAppTheme(
|
return applicationRepository.setAppTheme(
|
||||||
applicationId, theme.getId(), null, MANAGE_APPLICATIONS
|
application.getId(), theme.getId(), null, MANAGE_APPLICATIONS
|
||||||
).thenReturn(theme);
|
).thenReturn(theme);
|
||||||
} else {
|
} else {
|
||||||
return applicationRepository.setAppTheme(
|
return applicationRepository.setAppTheme(
|
||||||
applicationId, null, theme.getId(), MANAGE_APPLICATIONS
|
application.getId(), null, theme.getId(), MANAGE_APPLICATIONS
|
||||||
).thenReturn(theme);
|
).thenReturn(theme);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -204,7 +259,117 @@ public class ThemeServiceCEImpl extends BaseService<ThemeRepositoryCE, Theme, St
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetDefaultThemeIdCache() {
|
public Mono<Theme> persistCurrentTheme(String applicationId, Theme resource) {
|
||||||
defaultThemeId = null;
|
return applicationRepository.findById(applicationId, MANAGE_APPLICATIONS)
|
||||||
|
.switchIfEmpty(Mono.error(
|
||||||
|
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, applicationId))
|
||||||
|
)
|
||||||
|
.flatMap(application -> {
|
||||||
|
String themeId = application.getEditModeThemeId();
|
||||||
|
if(!StringUtils.hasLength(themeId)) { // theme id is not present, raise error
|
||||||
|
return Mono.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION));
|
||||||
|
} else {
|
||||||
|
return repository.findById(themeId, READ_THEMES)
|
||||||
|
.map(theme -> Tuples.of(theme, application));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatMap(themeAndApplicationTuple -> {
|
||||||
|
Theme theme = themeAndApplicationTuple.getT1();
|
||||||
|
Application application = themeAndApplicationTuple.getT2();
|
||||||
|
theme.setId(null); // we'll create a copy so setting id to null
|
||||||
|
theme.setSystemTheme(false);
|
||||||
|
theme.setApplicationId(applicationId);
|
||||||
|
theme.setOrganizationId(application.getOrganizationId());
|
||||||
|
theme.setPolicies(policyGenerator.getAllChildPolicies(
|
||||||
|
application.getPolicies(), Application.class, Theme.class
|
||||||
|
));
|
||||||
|
|
||||||
|
if(StringUtils.hasLength(resource.getName())) {
|
||||||
|
theme.setName(resource.getName());
|
||||||
|
} else {
|
||||||
|
theme.setName(theme.getName() + " copy");
|
||||||
|
}
|
||||||
|
return repository.save(theme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will fetch a theme by id and delete this if it's not a system theme.
|
||||||
|
* When an app is published with a customized theme, we store a copy of that theme so that changes are available
|
||||||
|
* in published mode even user has changed the theme in edit mode. When user switches back to another theme and
|
||||||
|
* publish the application where that app was previously published with a custom theme, we should delete that copy.
|
||||||
|
* Otherwise there'll be a lot of orphan theme copies that were set a published mode once but are used no more.
|
||||||
|
* @param themeId id of the theme that'll be deleted
|
||||||
|
* @return deleted theme mono
|
||||||
|
*/
|
||||||
|
private Mono<Theme> deletePublishedCustomizedThemeCopy(String themeId) {
|
||||||
|
if(!StringUtils.hasLength(themeId)) {
|
||||||
|
return Mono.empty();
|
||||||
|
}
|
||||||
|
return repository.findById(themeId).flatMap(theme -> {
|
||||||
|
if(!theme.isSystemTheme()) {
|
||||||
|
return repository.deleteById(themeId).thenReturn(theme);
|
||||||
|
}
|
||||||
|
return Mono.just(theme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Theme> delete(String themeId) {
|
||||||
|
return repository.findById(themeId, MANAGE_THEMES)
|
||||||
|
.switchIfEmpty(Mono.error(
|
||||||
|
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, FieldName.THEME))
|
||||||
|
).flatMap(theme -> {
|
||||||
|
if (StringUtils.hasLength(theme.getApplicationId())) { // only persisted themes are allowed to delete
|
||||||
|
return repository.archive(theme);
|
||||||
|
} else {
|
||||||
|
return Mono.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Theme> getSystemTheme(String themeName) {
|
||||||
|
return repository.getSystemThemeByName(themeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Theme> getThemeById(String themeId, AclPermission permission) {
|
||||||
|
return repository.findById(themeId, permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Theme> save(Theme theme) {
|
||||||
|
return repository.save(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Theme> updateName(String id, Theme themeDto) {
|
||||||
|
return repository.findById(id, MANAGE_THEMES)
|
||||||
|
.switchIfEmpty(Mono.error(
|
||||||
|
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.THEME, id))
|
||||||
|
).flatMap(theme -> {
|
||||||
|
if(StringUtils.hasLength(themeDto.getName())) {
|
||||||
|
theme.setName(themeDto.getName());
|
||||||
|
}
|
||||||
|
return repository.save(theme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Theme> getOrSaveTheme(Theme theme, Application destApplication) {
|
||||||
|
if(theme == null) { // this application was exported without theme, assign the legacy theme to it
|
||||||
|
return repository.getSystemThemeByName(Theme.LEGACY_THEME_NAME); // return the default theme
|
||||||
|
} else if (theme.isSystemTheme()) {
|
||||||
|
return repository.getSystemThemeByName(theme.getName());
|
||||||
|
} else {
|
||||||
|
theme.setApplicationId(null);
|
||||||
|
theme.setOrganizationId(null);
|
||||||
|
theme.setPolicies(policyGenerator.getAllChildPolicies(
|
||||||
|
destApplication.getPolicies(), Application.class, Theme.class
|
||||||
|
));
|
||||||
|
return repository.save(theme);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.domains.NewPage;
|
import com.appsmith.server.domains.NewPage;
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
import com.appsmith.server.domains.Page;
|
import com.appsmith.server.domains.Page;
|
||||||
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.domains.UserRole;
|
import com.appsmith.server.domains.UserRole;
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
|
@ -172,6 +173,9 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
Map<String, Policy> commentThreadPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
Map<String, Policy> commentThreadPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
||||||
applicationPolicyMap, Application.class, CommentThread.class
|
applicationPolicyMap, Application.class, CommentThread.class
|
||||||
);
|
);
|
||||||
|
Map<String, Policy> themePolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
||||||
|
applicationPolicyMap, Application.class, Theme.class
|
||||||
|
);
|
||||||
//Now update the organization policies
|
//Now update the organization policies
|
||||||
Organization updatedOrganization = policyUtils.addPoliciesToExistingObject(orgPolicyMap, organization);
|
Organization updatedOrganization = policyUtils.addPoliciesToExistingObject(orgPolicyMap, organization);
|
||||||
updatedOrganization.setUserRoles(userRoles);
|
updatedOrganization.setUserRoles(userRoles);
|
||||||
|
|
@ -179,7 +183,7 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
// Update the underlying application/page/action
|
// Update the underlying application/page/action
|
||||||
Flux<Datasource> updatedDatasourcesFlux = policyUtils.updateWithNewPoliciesToDatasourcesByOrgId(updatedOrganization.getId(), datasourcePolicyMap, true);
|
Flux<Datasource> updatedDatasourcesFlux = policyUtils.updateWithNewPoliciesToDatasourcesByOrgId(updatedOrganization.getId(), datasourcePolicyMap, true);
|
||||||
Flux<Application> updatedApplicationsFlux = policyUtils.updateWithNewPoliciesToApplicationsByOrgId(updatedOrganization.getId(), applicationPolicyMap, true)
|
Flux<Application> updatedApplicationsFlux = policyUtils.updateWithNewPoliciesToApplicationsByOrgId(updatedOrganization.getId(), applicationPolicyMap, true)
|
||||||
.cache();
|
.cache(); // .cache is very important, as we will execute once and reuse the results multiple times
|
||||||
Flux<NewPage> updatedPagesFlux = updatedApplicationsFlux
|
Flux<NewPage> updatedPagesFlux = updatedApplicationsFlux
|
||||||
.flatMap(application -> policyUtils.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, true));
|
.flatMap(application -> policyUtils.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, true));
|
||||||
Flux<NewAction> updatedActionsFlux = updatedApplicationsFlux
|
Flux<NewAction> updatedActionsFlux = updatedApplicationsFlux
|
||||||
|
|
@ -188,14 +192,18 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
.flatMap(application -> policyUtils.updateWithPagePermissionsToAllItsActionCollections(application.getId(), actionPolicyMap, true));
|
.flatMap(application -> policyUtils.updateWithPagePermissionsToAllItsActionCollections(application.getId(), actionPolicyMap, true));
|
||||||
Flux<CommentThread> updatedThreadsFlux = updatedApplicationsFlux
|
Flux<CommentThread> updatedThreadsFlux = updatedApplicationsFlux
|
||||||
.flatMap(application -> policyUtils.updateCommentThreadPermissions(application.getId(), commentThreadPolicyMap, user.getUsername(), true));
|
.flatMap(application -> policyUtils.updateCommentThreadPermissions(application.getId(), commentThreadPolicyMap, user.getUsername(), true));
|
||||||
|
Flux<Theme> updatedThemesFlux = updatedApplicationsFlux
|
||||||
|
.flatMap(application -> policyUtils.updateThemePolicies(
|
||||||
|
application, themePolicyMap, true
|
||||||
|
));
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
updatedDatasourcesFlux.collectList(),
|
updatedDatasourcesFlux.collectList(),
|
||||||
updatedPagesFlux.collectList(),
|
updatedPagesFlux.collectList(),
|
||||||
updatedActionsFlux.collectList(),
|
updatedActionsFlux.collectList(),
|
||||||
updatedActionCollectionsFlux.collectList(),
|
updatedActionCollectionsFlux.collectList(),
|
||||||
Mono.just(updatedOrganization),
|
Mono.just(updatedOrganization),
|
||||||
updatedThreadsFlux.collectList()
|
updatedThreadsFlux.collectList(),
|
||||||
|
updatedThemesFlux.collectList()
|
||||||
)
|
)
|
||||||
.flatMap(tuple -> {
|
.flatMap(tuple -> {
|
||||||
//By now all the datasources/applications/pages/actions have been updated. Just save the organization now
|
//By now all the datasources/applications/pages/actions have been updated. Just save the organization now
|
||||||
|
|
@ -256,6 +264,9 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
Map<String, Policy> commentThreadPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
Map<String, Policy> commentThreadPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
||||||
applicationPolicyMap, Application.class, CommentThread.class
|
applicationPolicyMap, Application.class, CommentThread.class
|
||||||
);
|
);
|
||||||
|
Map<String, Policy> themePolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
||||||
|
applicationPolicyMap, Application.class, Theme.class
|
||||||
|
);
|
||||||
|
|
||||||
//Now update the organization policies
|
//Now update the organization policies
|
||||||
Organization updatedOrganization = policyUtils.removePoliciesFromExistingObject(orgPolicyMap, organization);
|
Organization updatedOrganization = policyUtils.removePoliciesFromExistingObject(orgPolicyMap, organization);
|
||||||
|
|
@ -264,7 +275,7 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
// Update the underlying application/page/action
|
// Update the underlying application/page/action
|
||||||
Flux<Datasource> updatedDatasourcesFlux = policyUtils.updateWithNewPoliciesToDatasourcesByOrgId(updatedOrganization.getId(), datasourcePolicyMap, false);
|
Flux<Datasource> updatedDatasourcesFlux = policyUtils.updateWithNewPoliciesToDatasourcesByOrgId(updatedOrganization.getId(), datasourcePolicyMap, false);
|
||||||
Flux<Application> updatedApplicationsFlux = policyUtils.updateWithNewPoliciesToApplicationsByOrgId(updatedOrganization.getId(), applicationPolicyMap, false)
|
Flux<Application> updatedApplicationsFlux = policyUtils.updateWithNewPoliciesToApplicationsByOrgId(updatedOrganization.getId(), applicationPolicyMap, false)
|
||||||
.cache();
|
.cache(); // .cache is very important, as we will execute once and reuse the results multiple times
|
||||||
Flux<NewPage> updatedPagesFlux = updatedApplicationsFlux
|
Flux<NewPage> updatedPagesFlux = updatedApplicationsFlux
|
||||||
.flatMap(application -> policyUtils.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, false));
|
.flatMap(application -> policyUtils.updateWithApplicationPermissionsToAllItsPages(application.getId(), pagePolicyMap, false));
|
||||||
Flux<NewAction> updatedActionsFlux = updatedApplicationsFlux
|
Flux<NewAction> updatedActionsFlux = updatedApplicationsFlux
|
||||||
|
|
@ -275,6 +286,10 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
.flatMap(application -> policyUtils.updateCommentThreadPermissions(
|
.flatMap(application -> policyUtils.updateCommentThreadPermissions(
|
||||||
application.getId(), commentThreadPolicyMap, user.getUsername(), false
|
application.getId(), commentThreadPolicyMap, user.getUsername(), false
|
||||||
));
|
));
|
||||||
|
Flux<Theme> updatedThemesFlux = updatedApplicationsFlux
|
||||||
|
.flatMap(application -> policyUtils.updateThemePolicies(
|
||||||
|
application, themePolicyMap, false
|
||||||
|
));
|
||||||
|
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
updatedDatasourcesFlux.collectList(),
|
updatedDatasourcesFlux.collectList(),
|
||||||
|
|
@ -282,7 +297,8 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
updatedActionsFlux.collectList(),
|
updatedActionsFlux.collectList(),
|
||||||
updatedActionCollectionsFlux.collectList(),
|
updatedActionCollectionsFlux.collectList(),
|
||||||
updatedThreadsFlux.collectList(),
|
updatedThreadsFlux.collectList(),
|
||||||
Mono.just(updatedOrganization)
|
Mono.just(updatedOrganization),
|
||||||
|
updatedThemesFlux.collectList()
|
||||||
).flatMap(tuple -> {
|
).flatMap(tuple -> {
|
||||||
//By now all the datasources/applications/pages/actions have been updated. Just save the organization now
|
//By now all the datasources/applications/pages/actions have been updated. Just save the organization now
|
||||||
Organization updatedOrgBeforeSave = tuple.getT6();
|
Organization updatedOrgBeforeSave = tuple.getT6();
|
||||||
|
|
@ -435,6 +451,9 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
applicationPolicyMap, Application.class, CommentThread.class
|
applicationPolicyMap, Application.class, CommentThread.class
|
||||||
);
|
);
|
||||||
Map<String, Policy> actionPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(pagePolicyMap, Page.class, Action.class);
|
Map<String, Policy> actionPolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(pagePolicyMap, Page.class, Action.class);
|
||||||
|
Map<String, Policy> themePolicyMap = policyUtils.generateInheritedPoliciesFromSourcePolicies(
|
||||||
|
applicationPolicyMap, Application.class, Theme.class
|
||||||
|
);
|
||||||
|
|
||||||
// Now update the organization policies
|
// Now update the organization policies
|
||||||
Organization updatedOrganization = policyUtils.addPoliciesToExistingObject(orgPolicyMap, organization);
|
Organization updatedOrganization = policyUtils.addPoliciesToExistingObject(orgPolicyMap, organization);
|
||||||
|
|
@ -454,13 +473,19 @@ public class UserOrganizationServiceCEImpl implements UserOrganizationServiceCE
|
||||||
.flatMap(application -> policyUtils.updateCommentThreadPermissions(
|
.flatMap(application -> policyUtils.updateCommentThreadPermissions(
|
||||||
application.getId(), commentThreadPolicyMap, null, true
|
application.getId(), commentThreadPolicyMap, null, true
|
||||||
));
|
));
|
||||||
|
Flux<Theme> updatedThemesFlux = updatedApplicationsFlux
|
||||||
|
.flatMap(application -> policyUtils.updateThemePolicies(
|
||||||
|
application, themePolicyMap, true
|
||||||
|
));
|
||||||
|
|
||||||
return Mono.when(
|
return Mono.when(
|
||||||
updatedDatasourcesFlux.collectList(),
|
updatedDatasourcesFlux.collectList(),
|
||||||
updatedPagesFlux.collectList(),
|
updatedPagesFlux.collectList(),
|
||||||
updatedActionsFlux.collectList(),
|
updatedActionsFlux.collectList(),
|
||||||
updatedActionCollectionsFlux.collectList(),
|
updatedActionCollectionsFlux.collectList(),
|
||||||
updatedThreadsFlux.collectList())
|
updatedThreadsFlux.collectList(),
|
||||||
|
updatedThemesFlux.collectList()
|
||||||
|
)
|
||||||
// By now all the
|
// By now all the
|
||||||
// data sources/applications/pages/actions/action collections/comment threads
|
// data sources/applications/pages/actions/action collections/comment threads
|
||||||
// have been updated. Just save the organization now
|
// have been updated. Just save the organization now
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import com.appsmith.server.services.LayoutCollectionService;
|
||||||
import com.appsmith.server.services.NewActionService;
|
import com.appsmith.server.services.NewActionService;
|
||||||
import com.appsmith.server.services.OrganizationService;
|
import com.appsmith.server.services.OrganizationService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
|
import com.appsmith.server.services.ThemeService;
|
||||||
import com.appsmith.server.services.UserService;
|
import com.appsmith.server.services.UserService;
|
||||||
import com.appsmith.server.solutions.ce.ExamplesOrganizationClonerCEImpl;
|
import com.appsmith.server.solutions.ce.ExamplesOrganizationClonerCEImpl;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
@ -35,10 +36,11 @@ public class ExamplesOrganizationClonerImpl extends ExamplesOrganizationClonerCE
|
||||||
NewActionService newActionService,
|
NewActionService newActionService,
|
||||||
LayoutActionService layoutActionService,
|
LayoutActionService layoutActionService,
|
||||||
ActionCollectionService actionCollectionService,
|
ActionCollectionService actionCollectionService,
|
||||||
LayoutCollectionService layoutCollectionService) {
|
LayoutCollectionService layoutCollectionService,
|
||||||
|
ThemeService themeService) {
|
||||||
|
|
||||||
super(organizationService, organizationRepository, datasourceService, datasourceRepository, configService,
|
super(organizationService, organizationRepository, datasourceService, datasourceRepository, configService,
|
||||||
sessionUserService, userService, applicationService, applicationPageService, newPageRepository,
|
sessionUserService, userService, applicationService, applicationPageService, newPageRepository,
|
||||||
newActionService, layoutActionService, actionCollectionService, layoutCollectionService);
|
newActionService, layoutActionService, actionCollectionService, layoutCollectionService, themeService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import com.appsmith.server.repositories.DatasourceRepository;
|
||||||
import com.appsmith.server.repositories.NewActionRepository;
|
import com.appsmith.server.repositories.NewActionRepository;
|
||||||
import com.appsmith.server.repositories.NewPageRepository;
|
import com.appsmith.server.repositories.NewPageRepository;
|
||||||
import com.appsmith.server.repositories.PluginRepository;
|
import com.appsmith.server.repositories.PluginRepository;
|
||||||
import com.appsmith.server.repositories.ThemeRepository;
|
|
||||||
import com.appsmith.server.services.ActionCollectionService;
|
import com.appsmith.server.services.ActionCollectionService;
|
||||||
import com.appsmith.server.services.ApplicationPageService;
|
import com.appsmith.server.services.ApplicationPageService;
|
||||||
import com.appsmith.server.services.ApplicationService;
|
import com.appsmith.server.services.ApplicationService;
|
||||||
|
|
@ -15,6 +14,7 @@ import com.appsmith.server.services.NewPageService;
|
||||||
import com.appsmith.server.services.OrganizationService;
|
import com.appsmith.server.services.OrganizationService;
|
||||||
import com.appsmith.server.services.SequenceService;
|
import com.appsmith.server.services.SequenceService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
|
import com.appsmith.server.services.ThemeService;
|
||||||
import com.appsmith.server.solutions.ce.ImportExportApplicationServiceCEImpl;
|
import com.appsmith.server.solutions.ce.ImportExportApplicationServiceCEImpl;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
@ -38,11 +38,11 @@ public class ImportExportApplicationServiceImpl extends ImportExportApplicationS
|
||||||
ExamplesOrganizationCloner examplesOrganizationCloner,
|
ExamplesOrganizationCloner examplesOrganizationCloner,
|
||||||
ActionCollectionRepository actionCollectionRepository,
|
ActionCollectionRepository actionCollectionRepository,
|
||||||
ActionCollectionService actionCollectionService,
|
ActionCollectionService actionCollectionService,
|
||||||
ThemeRepository themeRepository) {
|
ThemeService themeService) {
|
||||||
|
|
||||||
super(datasourceService, sessionUserService, newActionRepository, datasourceRepository, pluginRepository,
|
super(datasourceService, sessionUserService, newActionRepository, datasourceRepository, pluginRepository,
|
||||||
organizationService, applicationService, newPageService, applicationPageService, newPageRepository,
|
organizationService, applicationService, newPageService, applicationPageService, newPageRepository,
|
||||||
newActionService, sequenceService, examplesOrganizationCloner, actionCollectionRepository,
|
newActionService, sequenceService, examplesOrganizationCloner, actionCollectionRepository,
|
||||||
actionCollectionService, themeRepository);
|
actionCollectionService, themeService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.appsmith.external.models.AuthenticationDTO;
|
||||||
import com.appsmith.external.models.BaseDomain;
|
import com.appsmith.external.models.BaseDomain;
|
||||||
import com.appsmith.external.models.Datasource;
|
import com.appsmith.external.models.Datasource;
|
||||||
import com.appsmith.external.models.DefaultResources;
|
import com.appsmith.external.models.DefaultResources;
|
||||||
|
import com.appsmith.server.acl.AclPermission;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
|
|
@ -13,6 +14,7 @@ import com.appsmith.server.domains.ApplicationPage;
|
||||||
import com.appsmith.server.domains.Layout;
|
import com.appsmith.server.domains.Layout;
|
||||||
import com.appsmith.server.domains.NewPage;
|
import com.appsmith.server.domains.NewPage;
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.dtos.ActionCollectionDTO;
|
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||||
import com.appsmith.server.dtos.ActionDTO;
|
import com.appsmith.server.dtos.ActionDTO;
|
||||||
|
|
@ -33,7 +35,9 @@ import com.appsmith.server.services.LayoutCollectionService;
|
||||||
import com.appsmith.server.services.NewActionService;
|
import com.appsmith.server.services.NewActionService;
|
||||||
import com.appsmith.server.services.OrganizationService;
|
import com.appsmith.server.services.OrganizationService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
|
import com.appsmith.server.services.ThemeService;
|
||||||
import com.appsmith.server.services.UserService;
|
import com.appsmith.server.services.UserService;
|
||||||
|
import com.mongodb.client.result.UpdateResult;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
|
|
@ -68,6 +72,7 @@ public class ExamplesOrganizationClonerCEImpl implements ExamplesOrganizationClo
|
||||||
private final LayoutActionService layoutActionService;
|
private final LayoutActionService layoutActionService;
|
||||||
private final ActionCollectionService actionCollectionService;
|
private final ActionCollectionService actionCollectionService;
|
||||||
private final LayoutCollectionService layoutCollectionService;
|
private final LayoutCollectionService layoutCollectionService;
|
||||||
|
private final ThemeService themeService;
|
||||||
|
|
||||||
public Mono<Organization> cloneExamplesOrganization() {
|
public Mono<Organization> cloneExamplesOrganization() {
|
||||||
return sessionUserService
|
return sessionUserService
|
||||||
|
|
@ -417,17 +422,35 @@ public class ExamplesOrganizationClonerCEImpl implements ExamplesOrganizationClo
|
||||||
.flatMapMany(
|
.flatMapMany(
|
||||||
savedApplication -> {
|
savedApplication -> {
|
||||||
applicationIds.add(savedApplication.getId());
|
applicationIds.add(savedApplication.getId());
|
||||||
return newPageRepository
|
return forkThemes(application, savedApplication).thenMany(
|
||||||
.findByApplicationId(templateApplicationId)
|
newPageRepository
|
||||||
.map(newPage -> {
|
.findByApplicationId(templateApplicationId)
|
||||||
log.info("Preparing page for cloning {} {}.", newPage.getUnpublishedPage().getName(), newPage.getId());
|
.map(newPage -> {
|
||||||
newPage.setApplicationId(savedApplication.getId());
|
log.info("Preparing page for cloning {} {}.", newPage.getUnpublishedPage().getName(), newPage.getId());
|
||||||
return newPage;
|
newPage.setApplicationId(savedApplication.getId());
|
||||||
});
|
return newPage;
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Mono<UpdateResult> forkThemes(Application srcApplication, Application destApplication) {
|
||||||
|
return Mono.zip(
|
||||||
|
themeService.cloneThemeToApplication(srcApplication.getEditModeThemeId(), destApplication),
|
||||||
|
themeService.cloneThemeToApplication(srcApplication.getPublishedModeThemeId(), destApplication)
|
||||||
|
).flatMap(themes -> {
|
||||||
|
Theme editModeTheme = themes.getT1();
|
||||||
|
Theme publishedModeTheme = themes.getT2();
|
||||||
|
return applicationService.setAppTheme(
|
||||||
|
destApplication.getId(),
|
||||||
|
editModeTheme.getId(),
|
||||||
|
publishedModeTheme.getId(),
|
||||||
|
AclPermission.MANAGE_APPLICATIONS
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Mono<Application> cloneApplicationDocument(Application application) {
|
private Mono<Application> cloneApplicationDocument(Application application) {
|
||||||
if (!StringUtils.hasText(application.getName())) {
|
if (!StringUtils.hasText(application.getName())) {
|
||||||
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.NAME));
|
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.NAME));
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ import com.appsmith.server.repositories.DatasourceRepository;
|
||||||
import com.appsmith.server.repositories.NewActionRepository;
|
import com.appsmith.server.repositories.NewActionRepository;
|
||||||
import com.appsmith.server.repositories.NewPageRepository;
|
import com.appsmith.server.repositories.NewPageRepository;
|
||||||
import com.appsmith.server.repositories.PluginRepository;
|
import com.appsmith.server.repositories.PluginRepository;
|
||||||
import com.appsmith.server.repositories.ThemeRepository;
|
|
||||||
import com.appsmith.server.services.ActionCollectionService;
|
import com.appsmith.server.services.ActionCollectionService;
|
||||||
import com.appsmith.server.services.ApplicationPageService;
|
import com.appsmith.server.services.ApplicationPageService;
|
||||||
import com.appsmith.server.services.ApplicationService;
|
import com.appsmith.server.services.ApplicationService;
|
||||||
|
|
@ -47,6 +46,7 @@ import com.appsmith.server.services.NewPageService;
|
||||||
import com.appsmith.server.services.OrganizationService;
|
import com.appsmith.server.services.OrganizationService;
|
||||||
import com.appsmith.server.services.SequenceService;
|
import com.appsmith.server.services.SequenceService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
|
import com.appsmith.server.services.ThemeService;
|
||||||
import com.appsmith.server.solutions.ExamplesOrganizationCloner;
|
import com.appsmith.server.solutions.ExamplesOrganizationCloner;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
@ -77,6 +77,7 @@ import static com.appsmith.server.acl.AclPermission.EXPORT_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
|
@ -97,7 +98,7 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
|
||||||
private final ExamplesOrganizationCloner examplesOrganizationCloner;
|
private final ExamplesOrganizationCloner examplesOrganizationCloner;
|
||||||
private final ActionCollectionRepository actionCollectionRepository;
|
private final ActionCollectionRepository actionCollectionRepository;
|
||||||
private final ActionCollectionService actionCollectionService;
|
private final ActionCollectionService actionCollectionService;
|
||||||
private final ThemeRepository themeRepository;
|
private final ThemeService themeService;
|
||||||
|
|
||||||
private static final Set<MediaType> ALLOWED_CONTENT_TYPES = Set.of(MediaType.APPLICATION_JSON);
|
private static final Set<MediaType> ALLOWED_CONTENT_TYPES = Set.of(MediaType.APPLICATION_JSON);
|
||||||
private static final String INVALID_JSON_FILE = "invalid json file";
|
private static final String INVALID_JSON_FILE = "invalid json file";
|
||||||
|
|
@ -153,8 +154,8 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
|
||||||
return plugin;
|
return plugin;
|
||||||
})
|
})
|
||||||
.then(applicationMono)
|
.then(applicationMono)
|
||||||
.flatMap(application -> themeRepository.findById(application.getEditModeThemeId())
|
.flatMap(application -> themeService.getThemeById(application.getEditModeThemeId(), READ_THEMES)
|
||||||
.zipWith(themeRepository.findById(application.getPublishedModeThemeId()))
|
.zipWith(themeService.getThemeById(application.getPublishedModeThemeId(), READ_THEMES))
|
||||||
.map(themesTuple -> {
|
.map(themesTuple -> {
|
||||||
Theme editModeTheme = exportTheme(themesTuple.getT1());
|
Theme editModeTheme = exportTheme(themesTuple.getT1());
|
||||||
Theme publishedModeTheme = exportTheme(themesTuple.getT2());
|
Theme publishedModeTheme = exportTheme(themesTuple.getT2());
|
||||||
|
|
@ -581,7 +582,6 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
|
||||||
pluginMap.put(pluginReference, plugin.getId());
|
pluginMap.put(pluginReference, plugin.getId());
|
||||||
return plugin;
|
return plugin;
|
||||||
})
|
})
|
||||||
.then(importThemes(importedApplication, importedDoc))
|
|
||||||
.then(organizationService.findById(organizationId, AclPermission.ORGANIZATION_MANAGE_APPLICATIONS))
|
.then(organizationService.findById(organizationId, AclPermission.ORGANIZATION_MANAGE_APPLICATIONS))
|
||||||
.switchIfEmpty(Mono.error(
|
.switchIfEmpty(Mono.error(
|
||||||
new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.ORGANIZATION, organizationId))
|
new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.ORGANIZATION, organizationId))
|
||||||
|
|
@ -707,6 +707,7 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
|
||||||
.then(applicationService.save(importedApplication));
|
.then(applicationService.save(importedApplication));
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
.flatMap(savedAPP -> importThemes(savedAPP, importedDoc))
|
||||||
.flatMap(savedApp -> {
|
.flatMap(savedApp -> {
|
||||||
importedApplication.setId(savedApp.getId());
|
importedApplication.setId(savedApp.getId());
|
||||||
if (savedApp.getGitApplicationMetadata() != null) {
|
if (savedApp.getGitApplicationMetadata() != null) {
|
||||||
|
|
@ -1524,26 +1525,23 @@ public class ImportExportApplicationServiceCEImpl implements ImportExportApplica
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Application> importThemes(Application application, ApplicationJson importedApplicationJson) {
|
private Mono<Application> importThemes(Application application, ApplicationJson importedApplicationJson) {
|
||||||
Mono<Theme> importedEditModeTheme = getOrSaveTheme(importedApplicationJson.getEditModeTheme());
|
Mono<Theme> importedEditModeTheme = themeService.getOrSaveTheme(importedApplicationJson.getEditModeTheme(), application);
|
||||||
Mono<Theme> importedPublishedModeTheme = getOrSaveTheme(importedApplicationJson.getPublishedTheme());
|
Mono<Theme> importedPublishedModeTheme = themeService.getOrSaveTheme(importedApplicationJson.getPublishedTheme(), application);
|
||||||
|
|
||||||
return Mono.zip(importedEditModeTheme, importedPublishedModeTheme).map(importedThemesTuple -> {
|
return Mono.zip(importedEditModeTheme, importedPublishedModeTheme).flatMap(importedThemesTuple -> {
|
||||||
application.setEditModeThemeId(importedThemesTuple.getT1().getId());
|
String editModeThemeId = importedThemesTuple.getT1().getId();
|
||||||
application.setPublishedModeThemeId(importedThemesTuple.getT2().getId());
|
String publishedModeThemeId = importedThemesTuple.getT2().getId();
|
||||||
return application;
|
|
||||||
|
application.setEditModeThemeId(editModeThemeId);
|
||||||
|
application.setPublishedModeThemeId(publishedModeThemeId);
|
||||||
|
// this will update the theme id in DB
|
||||||
|
// also returning the updated application object so that theme id are available to the next pipeline
|
||||||
|
return applicationService.setAppTheme(
|
||||||
|
application.getId(), editModeThemeId, publishedModeThemeId, MANAGE_APPLICATIONS
|
||||||
|
).thenReturn(application);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Theme> getOrSaveTheme(Theme theme) {
|
|
||||||
if(theme == null) { // this application was exported without theme, assign the legacy theme to it
|
|
||||||
return themeRepository.getSystemThemeByName(Theme.LEGACY_THEME_NAME); // return the default theme
|
|
||||||
} else if (theme.isSystemTheme()) {
|
|
||||||
return themeRepository.getSystemThemeByName(theme.getName());
|
|
||||||
} else {
|
|
||||||
return themeRepository.save(theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeUnwantedFieldsFromApplicationDuringExport(Application application) {
|
private void removeUnwantedFieldsFromApplicationDuringExport(Application application) {
|
||||||
application.setOrganizationId(null);
|
application.setOrganizationId(null);
|
||||||
application.setPages(null);
|
application.setPages(null);
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@
|
||||||
"appBorderRadius": {
|
"appBorderRadius": {
|
||||||
"none": "0px",
|
"none": "0px",
|
||||||
"md": "0.375rem",
|
"md": "0.375rem",
|
||||||
"lg": "1.5rem",
|
"lg": "1.5rem"
|
||||||
"full": "9999px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"boxShadow": {
|
"boxShadow": {
|
||||||
|
|
@ -41,135 +40,216 @@
|
||||||
"AUDIO_RECORDER_WIDGET": {
|
"AUDIO_RECORDER_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_WIDGET": {
|
"BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_GROUP_WIDGET": {
|
"BUTTON_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"CAMERA_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHART_WIDGET": {
|
"CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"CHECKBOX_WIDGET": {
|
"CHECKBOX_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHECKBOX_GROUP_WIDGET": {
|
"CHECKBOX_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CONTAINER_WIDGET": {
|
"CONTAINER_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
|
"CIRCULAR_PROGRESS_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"CURRENCY_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"PHONE_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"DATE_PICKER_WIDGET2": {
|
"DATE_PICKER_WIDGET2": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FILE_PICKER_WIDGET_V2": {
|
"FILE_PICKER_WIDGET_V2": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FORM_WIDGET": {
|
"FORM_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"FORM_BUTTON_WIDGET": {
|
"FORM_BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"ICON_BUTTON_WIDGET": {
|
"ICON_BUTTON_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"IFRAME_WIDGET": {
|
"IFRAME_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"IMAGE_WIDGET": {
|
"IMAGE_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"INPUT_WIDGET": {
|
"INPUT_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"INPUT_WIDGET_V2": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"LIST_WIDGET": {
|
"LIST_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MAP_WIDGET": {
|
"MAP_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
|
},
|
||||||
|
"MAP_CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MENU_BUTTON_WIDGET": {
|
"MENU_BUTTON_WIDGET": {
|
||||||
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MODAL_WIDGET": {
|
"MODAL_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_TREE_WIDGET": {
|
"MULTI_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_WIDGET": {
|
"MULTI_SELECT_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"DROP_DOWN_WIDGET": {
|
"DROP_DOWN_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
|
"PROGRESSBAR_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"RATE_WIDGET": {
|
||||||
|
"activeColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
|
},
|
||||||
"RADIO_GROUP_WIDGET": {
|
"RADIO_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"RICH_TEXT_EDITOR_WIDGET": {
|
"RICH_TEXT_EDITOR_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"STATBOX_WIDGET": {
|
"STATBOX_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SWITCH_WIDGET": {
|
"SWITCH_WIDGET": {
|
||||||
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"SWITCH_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
},
|
},
|
||||||
|
"SELECT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"TABLE_WIDGET": {
|
"TABLE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TABS_WIDGET": {
|
"TABS_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TEXT_WIDGET": {
|
"TEXT_WIDGET": {
|
||||||
},
|
},
|
||||||
"VIDEO_WIDGET": {
|
"VIDEO_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SINGLE_SELECT_TREE_WIDGET": {
|
"SINGLE_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -199,8 +279,7 @@
|
||||||
"appBorderRadius": {
|
"appBorderRadius": {
|
||||||
"none": "0px",
|
"none": "0px",
|
||||||
"md": "0.375rem",
|
"md": "0.375rem",
|
||||||
"lg": "1.5rem",
|
"lg": "1.5rem"
|
||||||
"full": "9999px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"boxShadow": {
|
"boxShadow": {
|
||||||
|
|
@ -230,135 +309,216 @@
|
||||||
"AUDIO_RECORDER_WIDGET": {
|
"AUDIO_RECORDER_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_WIDGET": {
|
"BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_GROUP_WIDGET": {
|
"BUTTON_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"CAMERA_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHART_WIDGET": {
|
"CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"CHECKBOX_WIDGET": {
|
"CHECKBOX_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHECKBOX_GROUP_WIDGET": {
|
"CHECKBOX_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CONTAINER_WIDGET": {
|
"CONTAINER_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
|
"CIRCULAR_PROGRESS_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"CURRENCY_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"PHONE_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"DATE_PICKER_WIDGET2": {
|
"DATE_PICKER_WIDGET2": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FILE_PICKER_WIDGET_V2": {
|
"FILE_PICKER_WIDGET_V2": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FORM_WIDGET": {
|
"FORM_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"FORM_BUTTON_WIDGET": {
|
"FORM_BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"ICON_BUTTON_WIDGET": {
|
"ICON_BUTTON_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"IFRAME_WIDGET": {
|
"IFRAME_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"IMAGE_WIDGET": {
|
"IMAGE_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"INPUT_WIDGET": {
|
"INPUT_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"INPUT_WIDGET_V2": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"LIST_WIDGET": {
|
"LIST_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MAP_WIDGET": {
|
"MAP_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
|
},
|
||||||
|
"MAP_CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MENU_BUTTON_WIDGET": {
|
"MENU_BUTTON_WIDGET": {
|
||||||
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MODAL_WIDGET": {
|
"MODAL_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_TREE_WIDGET": {
|
"MULTI_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_WIDGET": {
|
"MULTI_SELECT_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"DROP_DOWN_WIDGET": {
|
"DROP_DOWN_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
|
"PROGRESSBAR_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"RATE_WIDGET": {
|
||||||
|
"activeColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
|
},
|
||||||
"RADIO_GROUP_WIDGET": {
|
"RADIO_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"RICH_TEXT_EDITOR_WIDGET": {
|
"RICH_TEXT_EDITOR_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"STATBOX_WIDGET": {
|
"STATBOX_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SWITCH_WIDGET": {
|
"SWITCH_WIDGET": {
|
||||||
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"SWITCH_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
},
|
},
|
||||||
|
"SELECT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"TABLE_WIDGET": {
|
"TABLE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TABS_WIDGET": {
|
"TABS_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TEXT_WIDGET": {
|
"TEXT_WIDGET": {
|
||||||
},
|
},
|
||||||
"VIDEO_WIDGET": {
|
"VIDEO_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SINGLE_SELECT_TREE_WIDGET": {
|
"SINGLE_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -388,8 +548,7 @@
|
||||||
"appBorderRadius": {
|
"appBorderRadius": {
|
||||||
"none": "0px",
|
"none": "0px",
|
||||||
"md": "0.375rem",
|
"md": "0.375rem",
|
||||||
"lg": "1.5rem",
|
"lg": "1.5rem"
|
||||||
"full": "9999px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"boxShadow": {
|
"boxShadow": {
|
||||||
|
|
@ -419,135 +578,216 @@
|
||||||
"AUDIO_RECORDER_WIDGET": {
|
"AUDIO_RECORDER_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_WIDGET": {
|
"BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_GROUP_WIDGET": {
|
"BUTTON_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"CAMERA_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHART_WIDGET": {
|
"CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"CHECKBOX_WIDGET": {
|
"CHECKBOX_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHECKBOX_GROUP_WIDGET": {
|
"CHECKBOX_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CONTAINER_WIDGET": {
|
"CONTAINER_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
|
"CIRCULAR_PROGRESS_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"CURRENCY_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"PHONE_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"DATE_PICKER_WIDGET2": {
|
"DATE_PICKER_WIDGET2": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FILE_PICKER_WIDGET_V2": {
|
"FILE_PICKER_WIDGET_V2": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FORM_WIDGET": {
|
"FORM_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"FORM_BUTTON_WIDGET": {
|
"FORM_BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"ICON_BUTTON_WIDGET": {
|
"ICON_BUTTON_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"IFRAME_WIDGET": {
|
"IFRAME_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"IMAGE_WIDGET": {
|
"IMAGE_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"INPUT_WIDGET": {
|
"INPUT_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"INPUT_WIDGET_V2": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"LIST_WIDGET": {
|
"LIST_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MAP_WIDGET": {
|
"MAP_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
|
},
|
||||||
|
"MAP_CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MENU_BUTTON_WIDGET": {
|
"MENU_BUTTON_WIDGET": {
|
||||||
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MODAL_WIDGET": {
|
"MODAL_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_TREE_WIDGET": {
|
"MULTI_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_WIDGET": {
|
"MULTI_SELECT_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"DROP_DOWN_WIDGET": {
|
"DROP_DOWN_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
|
"PROGRESSBAR_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"RATE_WIDGET": {
|
||||||
|
"activeColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
|
},
|
||||||
"RADIO_GROUP_WIDGET": {
|
"RADIO_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"RICH_TEXT_EDITOR_WIDGET": {
|
"RICH_TEXT_EDITOR_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"STATBOX_WIDGET": {
|
"STATBOX_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SWITCH_WIDGET": {
|
"SWITCH_WIDGET": {
|
||||||
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"SWITCH_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
},
|
},
|
||||||
|
"SELECT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"TABLE_WIDGET": {
|
"TABLE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TABS_WIDGET": {
|
"TABS_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TEXT_WIDGET": {
|
"TEXT_WIDGET": {
|
||||||
},
|
},
|
||||||
"VIDEO_WIDGET": {
|
"VIDEO_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SINGLE_SELECT_TREE_WIDGET": {
|
"SINGLE_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -577,8 +817,7 @@
|
||||||
"appBorderRadius": {
|
"appBorderRadius": {
|
||||||
"none": "0px",
|
"none": "0px",
|
||||||
"md": "0.375rem",
|
"md": "0.375rem",
|
||||||
"lg": "1.5rem",
|
"lg": "1.5rem"
|
||||||
"full": "9999px"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"boxShadow": {
|
"boxShadow": {
|
||||||
|
|
@ -608,135 +847,216 @@
|
||||||
"AUDIO_RECORDER_WIDGET": {
|
"AUDIO_RECORDER_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_WIDGET": {
|
"BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"BUTTON_GROUP_WIDGET": {
|
"BUTTON_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"CAMERA_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHART_WIDGET": {
|
"CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"CHECKBOX_WIDGET": {
|
"CHECKBOX_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CHECKBOX_GROUP_WIDGET": {
|
"CHECKBOX_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"CONTAINER_WIDGET": {
|
"CONTAINER_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
|
"CIRCULAR_PROGRESS_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"CURRENCY_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"PHONE_INPUT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"DATE_PICKER_WIDGET2": {
|
"DATE_PICKER_WIDGET2": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FILE_PICKER_WIDGET_V2": {
|
"FILE_PICKER_WIDGET_V2": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"FORM_WIDGET": {
|
"FORM_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"FORM_BUTTON_WIDGET": {
|
"FORM_BUTTON_WIDGET": {
|
||||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"ICON_BUTTON_WIDGET": {
|
"ICON_BUTTON_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"IFRAME_WIDGET": {
|
"IFRAME_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"IMAGE_WIDGET": {
|
"IMAGE_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"INPUT_WIDGET": {
|
"INPUT_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"INPUT_WIDGET_V2": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"LIST_WIDGET": {
|
"LIST_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MAP_WIDGET": {
|
"MAP_WIDGET": {
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
|
},
|
||||||
|
"MAP_CHART_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"MENU_BUTTON_WIDGET": {
|
"MENU_BUTTON_WIDGET": {
|
||||||
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
"menuColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MODAL_WIDGET": {
|
"MODAL_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_TREE_WIDGET": {
|
"MULTI_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"MULTI_SELECT_WIDGET": {
|
"MULTI_SELECT_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"DROP_DOWN_WIDGET": {
|
"DROP_DOWN_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "none"
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
|
"PROGRESSBAR_WIDGET": {
|
||||||
|
"fillColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||||
|
},
|
||||||
|
"RATE_WIDGET": {
|
||||||
|
"activeColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
|
},
|
||||||
"RADIO_GROUP_WIDGET": {
|
"RADIO_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
},
|
},
|
||||||
"RICH_TEXT_EDITOR_WIDGET": {
|
"RICH_TEXT_EDITOR_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"STATBOX_WIDGET": {
|
"STATBOX_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SWITCH_WIDGET": {
|
"SWITCH_WIDGET": {
|
||||||
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
|
"SWITCH_GROUP_WIDGET": {
|
||||||
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
"backgroundColor": "{{appsmith.theme.colors.primaryColor}}"
|
||||||
},
|
},
|
||||||
|
"SELECT_WIDGET": {
|
||||||
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
|
},
|
||||||
"TABLE_WIDGET": {
|
"TABLE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TABS_WIDGET": {
|
"TABS_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"TEXT_WIDGET": {
|
"TEXT_WIDGET": {
|
||||||
},
|
},
|
||||||
"VIDEO_WIDGET": {
|
"VIDEO_WIDGET": {
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}"
|
||||||
},
|
},
|
||||||
"SINGLE_SELECT_TREE_WIDGET": {
|
"SINGLE_SELECT_TREE_WIDGET": {
|
||||||
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
"primaryColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||||
|
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||||
|
"boxShadow": "none"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -745,7 +1065,7 @@
|
||||||
"backgroundColor": "#F6F6F6"
|
"backgroundColor": "#F6F6F6"
|
||||||
},
|
},
|
||||||
"borderRadius": {
|
"borderRadius": {
|
||||||
"appBorderRadius": "1rem"
|
"appBorderRadius": "1.5rem"
|
||||||
},
|
},
|
||||||
"boxShadow": {
|
"boxShadow": {
|
||||||
"appBoxShadow": "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)"
|
"appBoxShadow": "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)"
|
||||||
|
|
@ -755,4 +1075,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -46,7 +46,7 @@ public class PolicyUtilsTest {
|
||||||
CommentThread commentThread = new CommentThread();
|
CommentThread commentThread = new CommentThread();
|
||||||
commentThread.setApplicationId(testApplicationId);
|
commentThread.setApplicationId(testApplicationId);
|
||||||
Map<String, Policy> commentThreadPolicies = policyUtils.generatePolicyFromPermission(
|
Map<String, Policy> commentThreadPolicies = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_THREAD, AclPermission.COMMENT_ON_THREAD), "api_user"
|
Set.of(AclPermission.MANAGE_THREADS, AclPermission.COMMENT_ON_THREADS), "api_user"
|
||||||
);
|
);
|
||||||
commentThread.setPolicies(Set.copyOf(commentThreadPolicies.values()));
|
commentThread.setPolicies(Set.copyOf(commentThreadPolicies.values()));
|
||||||
Mono<CommentThread> saveThreadMono = commentThreadRepository.save(commentThread);
|
Mono<CommentThread> saveThreadMono = commentThreadRepository.save(commentThread);
|
||||||
|
|
@ -54,7 +54,7 @@ public class PolicyUtilsTest {
|
||||||
// add a new user and update the policies of the new user
|
// add a new user and update the policies of the new user
|
||||||
String newUserName = "new_test_user";
|
String newUserName = "new_test_user";
|
||||||
Map<String, Policy> commentThreadPoliciesForNewUser = policyUtils.generatePolicyFromPermission(
|
Map<String, Policy> commentThreadPoliciesForNewUser = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.COMMENT_ON_THREAD), newUserName
|
Set.of(AclPermission.COMMENT_ON_THREADS), newUserName
|
||||||
);
|
);
|
||||||
Flux<CommentThread> updateCommentThreads = policyUtils.updateCommentThreadPermissions(
|
Flux<CommentThread> updateCommentThreads = policyUtils.updateCommentThreadPermissions(
|
||||||
testApplicationId, commentThreadPoliciesForNewUser, newUserName, true
|
testApplicationId, commentThreadPoliciesForNewUser, newUserName, true
|
||||||
|
|
@ -64,7 +64,7 @@ public class PolicyUtilsTest {
|
||||||
Mono<List<CommentThread>> applicationCommentList = saveThreadMono
|
Mono<List<CommentThread>> applicationCommentList = saveThreadMono
|
||||||
.thenMany(updateCommentThreads)
|
.thenMany(updateCommentThreads)
|
||||||
.collectList()
|
.collectList()
|
||||||
.thenMany(commentThreadRepository.findByApplicationId(testApplicationId, AclPermission.READ_THREAD))
|
.thenMany(commentThreadRepository.findByApplicationId(testApplicationId, AclPermission.READ_THREADS))
|
||||||
.collectList();
|
.collectList();
|
||||||
|
|
||||||
StepVerifier.create(applicationCommentList)
|
StepVerifier.create(applicationCommentList)
|
||||||
|
|
@ -72,12 +72,12 @@ public class PolicyUtilsTest {
|
||||||
assertThat(commentThreads.size()).isEqualTo(1);
|
assertThat(commentThreads.size()).isEqualTo(1);
|
||||||
CommentThread commentThread1 = commentThreads.get(0);
|
CommentThread commentThread1 = commentThreads.get(0);
|
||||||
Set<Policy> policies = commentThread1.getPolicies();
|
Set<Policy> policies = commentThread1.getPolicies();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREAD.getValue(), "api_user")).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREADS.getValue(), "api_user")).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREAD.getValue(), newUserName)).isFalse();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREADS.getValue(), newUserName)).isFalse();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREAD.getValue(), "api_user")).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREADS.getValue(), "api_user")).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREAD.getValue(), newUserName)).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREADS.getValue(), newUserName)).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREAD.getValue(), "api_user")).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREADS.getValue(), "api_user")).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREAD.getValue(), newUserName)).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREADS.getValue(), newUserName)).isTrue();
|
||||||
})
|
})
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ public class PolicyUtilsTest {
|
||||||
user2.setEmail(newUserName);
|
user2.setEmail(newUserName);
|
||||||
|
|
||||||
Map<String, Policy> commentThreadPolicies = policyUtils.generatePolicyFromPermissionForMultipleUsers(
|
Map<String, Policy> commentThreadPolicies = policyUtils.generatePolicyFromPermissionForMultipleUsers(
|
||||||
Set.of(AclPermission.MANAGE_THREAD, AclPermission.COMMENT_ON_THREAD), List.of(user1, user2)
|
Set.of(AclPermission.MANAGE_THREADS, AclPermission.COMMENT_ON_THREADS), List.of(user1, user2)
|
||||||
);
|
);
|
||||||
|
|
||||||
commentThread.setPolicies(Set.copyOf(commentThreadPolicies.values()));
|
commentThread.setPolicies(Set.copyOf(commentThreadPolicies.values()));
|
||||||
|
|
@ -108,7 +108,7 @@ public class PolicyUtilsTest {
|
||||||
|
|
||||||
// remove an user and update the policies of the user
|
// remove an user and update the policies of the user
|
||||||
Map<String, Policy> commentThreadPoliciesForNewUser = policyUtils.generatePolicyFromPermission(
|
Map<String, Policy> commentThreadPoliciesForNewUser = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_THREAD, AclPermission.COMMENT_ON_THREAD), newUserName
|
Set.of(AclPermission.MANAGE_THREADS, AclPermission.COMMENT_ON_THREADS), newUserName
|
||||||
);
|
);
|
||||||
Flux<CommentThread> updateCommentThreads = policyUtils.updateCommentThreadPermissions(
|
Flux<CommentThread> updateCommentThreads = policyUtils.updateCommentThreadPermissions(
|
||||||
testApplicationId, commentThreadPoliciesForNewUser, newUserName, false
|
testApplicationId, commentThreadPoliciesForNewUser, newUserName, false
|
||||||
|
|
@ -118,7 +118,7 @@ public class PolicyUtilsTest {
|
||||||
Mono<List<CommentThread>> applicationCommentList = saveThreadMono
|
Mono<List<CommentThread>> applicationCommentList = saveThreadMono
|
||||||
.thenMany(updateCommentThreads)
|
.thenMany(updateCommentThreads)
|
||||||
.collectList()
|
.collectList()
|
||||||
.thenMany(commentThreadRepository.findByApplicationId(testApplicationId, AclPermission.READ_THREAD))
|
.thenMany(commentThreadRepository.findByApplicationId(testApplicationId, AclPermission.READ_THREADS))
|
||||||
.collectList();
|
.collectList();
|
||||||
|
|
||||||
StepVerifier.create(applicationCommentList)
|
StepVerifier.create(applicationCommentList)
|
||||||
|
|
@ -126,12 +126,12 @@ public class PolicyUtilsTest {
|
||||||
assertThat(commentThreads.size()).isEqualTo(1);
|
assertThat(commentThreads.size()).isEqualTo(1);
|
||||||
CommentThread commentThread1 = commentThreads.get(0);
|
CommentThread commentThread1 = commentThreads.get(0);
|
||||||
Set<Policy> policies = commentThread1.getPolicies();
|
Set<Policy> policies = commentThread1.getPolicies();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREAD.getValue(), "api_user")).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREADS.getValue(), "api_user")).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREAD.getValue(), newUserName)).isFalse();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.MANAGE_THREADS.getValue(), newUserName)).isFalse();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREAD.getValue(), "api_user")).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREADS.getValue(), "api_user")).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREAD.getValue(), newUserName)).isFalse();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.READ_THREADS.getValue(), newUserName)).isFalse();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREAD.getValue(), "api_user")).isTrue();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREADS.getValue(), "api_user")).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREAD.getValue(), newUserName)).isFalse();
|
assertThat(policyUtils.isPermissionPresentForUser(policies, AclPermission.COMMENT_ON_THREADS.getValue(), newUserName)).isFalse();
|
||||||
})
|
})
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ public class CustomCommentThreadRepositoryImplTest {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setEmail(userEmail);
|
user.setEmail(userEmail);
|
||||||
|
|
||||||
Map<String, Policy> policyMap = policyUtils.generatePolicyFromPermission(Set.of(AclPermission.READ_THREAD), user);
|
Map<String, Policy> policyMap = policyUtils.generatePolicyFromPermission(Set.of(AclPermission.READ_THREADS), user);
|
||||||
thread.setPolicies(Set.copyOf(policyMap.values()));
|
thread.setPolicies(Set.copyOf(policyMap.values()));
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +54,7 @@ public class CustomCommentThreadRepositoryImplTest {
|
||||||
user.setEmail(userEmail);
|
user.setEmail(userEmail);
|
||||||
|
|
||||||
Map<String, Policy> policyMap = policyUtils.generatePolicyFromPermission(
|
Map<String, Policy> policyMap = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.MANAGE_THREAD, AclPermission.COMMENT_ON_THREAD), user);
|
Set.of(AclPermission.MANAGE_THREADS, AclPermission.COMMENT_ON_THREADS), user);
|
||||||
HashSet<Policy> policySet = new HashSet<>();
|
HashSet<Policy> policySet = new HashSet<>();
|
||||||
|
|
||||||
// not using Set.of here because the caller function may need to add more policies
|
// not using Set.of here because the caller function may need to add more policies
|
||||||
|
|
@ -167,7 +167,7 @@ public class CustomCommentThreadRepositoryImplTest {
|
||||||
CommentThreadFilterDTO filterDTO = new CommentThreadFilterDTO();
|
CommentThreadFilterDTO filterDTO = new CommentThreadFilterDTO();
|
||||||
filterDTO.setApplicationId("sample-application-id-1");
|
filterDTO.setApplicationId("sample-application-id-1");
|
||||||
filterDTO.setResolved(false);
|
filterDTO.setResolved(false);
|
||||||
return commentThreadRepository.find(filterDTO, AclPermission.READ_THREAD).collectList();
|
return commentThreadRepository.find(filterDTO, AclPermission.READ_THREADS).collectList();
|
||||||
});
|
});
|
||||||
|
|
||||||
StepVerifier.create(listMono).assertNext(
|
StepVerifier.create(listMono).assertNext(
|
||||||
|
|
@ -255,7 +255,7 @@ public class CustomCommentThreadRepositoryImplTest {
|
||||||
Mono<Map<String, Collection<CommentThread>>> pageIdThreadMono = commentThreadRepository.saveAll(threads)
|
Mono<Map<String, Collection<CommentThread>>> pageIdThreadMono = commentThreadRepository.saveAll(threads)
|
||||||
.collectList()
|
.collectList()
|
||||||
.then(commentThreadRepository.archiveByPageId(pageOneId, ApplicationMode.EDIT))
|
.then(commentThreadRepository.archiveByPageId(pageOneId, ApplicationMode.EDIT))
|
||||||
.thenMany(commentThreadRepository.findByApplicationId(applicationId, AclPermission.READ_THREAD))
|
.thenMany(commentThreadRepository.findByApplicationId(applicationId, AclPermission.READ_THREADS))
|
||||||
.collectMultimap(CommentThread::getPageId);
|
.collectMultimap(CommentThread::getPageId);
|
||||||
|
|
||||||
StepVerifier.create(pageIdThreadMono)
|
StepVerifier.create(pageIdThreadMono)
|
||||||
|
|
@ -282,7 +282,7 @@ public class CustomCommentThreadRepositoryImplTest {
|
||||||
|
|
||||||
// add api_user to thread policy with read thread permission
|
// add api_user to thread policy with read thread permission
|
||||||
for(Policy policy: thread.getPolicies()) {
|
for(Policy policy: thread.getPolicies()) {
|
||||||
if(policy.getPermission().equals(AclPermission.READ_THREAD.getValue())) {
|
if(policy.getPermission().equals(AclPermission.READ_THREADS.getValue())) {
|
||||||
Set<String> users = new HashSet<>();
|
Set<String> users = new HashSet<>();
|
||||||
users.addAll(policy.getUsers());
|
users.addAll(policy.getUsers());
|
||||||
users.add("api_user");
|
users.add("api_user");
|
||||||
|
|
@ -292,7 +292,7 @@ public class CustomCommentThreadRepositoryImplTest {
|
||||||
|
|
||||||
Mono<Map<String, Collection<CommentThread>>> pageIdThreadMono = commentThreadRepository.save(thread)
|
Mono<Map<String, Collection<CommentThread>>> pageIdThreadMono = commentThreadRepository.save(thread)
|
||||||
.then(commentThreadRepository.archiveByPageId(testPageId, ApplicationMode.EDIT)) // this will do nothing
|
.then(commentThreadRepository.archiveByPageId(testPageId, ApplicationMode.EDIT)) // this will do nothing
|
||||||
.thenMany(commentThreadRepository.findByApplicationId(applicationId, AclPermission.READ_THREAD))
|
.thenMany(commentThreadRepository.findByApplicationId(applicationId, AclPermission.READ_THREADS))
|
||||||
.collectMultimap(CommentThread::getPageId);
|
.collectMultimap(CommentThread::getPageId);
|
||||||
|
|
||||||
StepVerifier.create(pageIdThreadMono)
|
StepVerifier.create(pageIdThreadMono)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.appsmith.server.repositories;
|
package com.appsmith.server.repositories;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.Policy;
|
||||||
import com.appsmith.server.domains.Theme;
|
import com.appsmith.server.domains.Theme;
|
||||||
|
import com.appsmith.server.helpers.PolicyUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -11,7 +13,10 @@ import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
|
|
@ -21,10 +26,20 @@ public class CustomThemeRepositoryTest {
|
||||||
@Autowired
|
@Autowired
|
||||||
ThemeRepository themeRepository;
|
ThemeRepository themeRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
PolicyUtils policyUtils;
|
||||||
|
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void getSystemThemes_WhenThemesExists_ReturnsSystemThemes() {
|
public void getSystemThemes_WhenThemesExists_ReturnsSystemThemes() {
|
||||||
Mono<List<Theme>> systemThemesMono = themeRepository.save(new Theme())
|
String testAppId = "second-app-id";
|
||||||
|
Theme firstAppTheme = new Theme();
|
||||||
|
firstAppTheme.setApplicationId("first-app-id");
|
||||||
|
|
||||||
|
Theme secondAppTheme = new Theme();
|
||||||
|
secondAppTheme.setApplicationId(testAppId);
|
||||||
|
|
||||||
|
Mono<List<Theme>> systemThemesMono = themeRepository.saveAll(List.of(firstAppTheme, secondAppTheme))
|
||||||
.then(themeRepository.getSystemThemes().collectList());
|
.then(themeRepository.getSystemThemes().collectList());
|
||||||
|
|
||||||
StepVerifier.create(systemThemesMono).assertNext(themes -> {
|
StepVerifier.create(systemThemesMono).assertNext(themes -> {
|
||||||
|
|
@ -32,6 +47,28 @@ public class CustomThemeRepositoryTest {
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void getApplicationThemes_WhenThemesExists_ReturnsAppThemes() {
|
||||||
|
Map<String, Policy> themePolicies = policyUtils.generatePolicyFromPermission(Set.of(READ_THEMES), "api_user");
|
||||||
|
|
||||||
|
String testAppId = "second-app-id";
|
||||||
|
Theme firstAppTheme = new Theme();
|
||||||
|
firstAppTheme.setApplicationId("first-app-id");
|
||||||
|
firstAppTheme.setPolicies(Set.of(themePolicies.get(READ_THEMES.getValue())));
|
||||||
|
|
||||||
|
Theme secondAppTheme = new Theme();
|
||||||
|
secondAppTheme.setApplicationId(testAppId);
|
||||||
|
secondAppTheme.setPolicies(Set.of(themePolicies.get(READ_THEMES.getValue())));
|
||||||
|
|
||||||
|
Mono<List<Theme>> systemThemesMono = themeRepository.saveAll(List.of(firstAppTheme, secondAppTheme))
|
||||||
|
.then(themeRepository.getApplicationThemes(testAppId, READ_THEMES).collectList());
|
||||||
|
|
||||||
|
StepVerifier.create(systemThemesMono).assertNext(themes -> {
|
||||||
|
assertThat(themes.size()).isEqualTo(5); // 4 system themes were created from db migration
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void getSystemThemeByName_WhenNameMatches_ReturnsTheme() {
|
public void getSystemThemeByName_WhenNameMatches_ReturnsTheme() {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import com.appsmith.server.domains.NewPage;
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
import com.appsmith.server.domains.Plugin;
|
import com.appsmith.server.domains.Plugin;
|
||||||
import com.appsmith.server.domains.PluginType;
|
import com.appsmith.server.domains.PluginType;
|
||||||
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.dtos.ActionCollectionDTO;
|
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||||
import com.appsmith.server.dtos.ActionDTO;
|
import com.appsmith.server.dtos.ActionDTO;
|
||||||
|
|
@ -82,6 +83,7 @@ import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.MANAGE_THEMES;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
|
import static com.appsmith.server.acl.AclPermission.READ_DATASOURCES;
|
||||||
|
|
@ -2252,4 +2254,42 @@ public class ApplicationServiceTest {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUserDetails(value = "api_user")
|
||||||
|
public void cloneApplication_WithCustomSavedTheme_ThemesAlsoCopied() {
|
||||||
|
Application testApplication = new Application();
|
||||||
|
String appName = "cloneApplication_WithCustomSavedTheme_ThemesAlsoCopied";
|
||||||
|
testApplication.setName(appName);
|
||||||
|
|
||||||
|
Theme theme = new Theme();
|
||||||
|
theme.setName("Custom theme");
|
||||||
|
|
||||||
|
Mono<Theme> createTheme = themeService.create(theme);
|
||||||
|
|
||||||
|
Mono<Tuple2<Theme, Tuple2<Application, Application>>> tuple2Application = createTheme
|
||||||
|
.then(applicationPageService.createApplication(testApplication, orgId))
|
||||||
|
.flatMap(application ->
|
||||||
|
themeService.updateTheme(application.getId(), theme).then(
|
||||||
|
themeService.persistCurrentTheme(application.getId(), new Theme())
|
||||||
|
.flatMap(theme1 -> Mono.zip(
|
||||||
|
applicationPageService.cloneApplication(application.getId(), null),
|
||||||
|
Mono.just(application))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).flatMap(objects ->
|
||||||
|
themeService.getThemeById(objects.getT1().getEditModeThemeId(), MANAGE_THEMES)
|
||||||
|
.zipWith(Mono.just(objects))
|
||||||
|
);
|
||||||
|
|
||||||
|
StepVerifier.create(tuple2Application)
|
||||||
|
.assertNext(objects -> {
|
||||||
|
Theme clonedTheme = objects.getT1();
|
||||||
|
Application clonedApp = objects.getT2().getT1();
|
||||||
|
Application srcApp = objects.getT2().getT2();
|
||||||
|
assertThat(clonedApp.getEditModeThemeId()).isNotEqualTo(srcApp.getEditModeThemeId());
|
||||||
|
assertThat(clonedTheme.getApplicationId()).isNull();
|
||||||
|
assertThat(clonedTheme.getOrganizationId()).isNull();
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,15 +141,15 @@ public class CommentServiceTest {
|
||||||
assertThat(thread.getId()).isNotEmpty();
|
assertThat(thread.getId()).isNotEmpty();
|
||||||
//assertThat(thread.getResolved()).isNull();
|
//assertThat(thread.getResolved()).isNull();
|
||||||
assertThat(thread.getPolicies()).containsExactlyInAnyOrder(
|
assertThat(thread.getPolicies()).containsExactlyInAnyOrder(
|
||||||
Policy.builder().permission(AclPermission.READ_THREAD.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build(),
|
Policy.builder().permission(AclPermission.READ_THREADS.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build(),
|
||||||
Policy.builder().permission(AclPermission.MANAGE_THREAD.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build(),
|
Policy.builder().permission(AclPermission.MANAGE_THREADS.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build(),
|
||||||
Policy.builder().permission(AclPermission.COMMENT_ON_THREAD.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build()
|
Policy.builder().permission(AclPermission.COMMENT_ON_THREADS.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build()
|
||||||
);
|
);
|
||||||
assertThat(thread.getComments()).hasSize(2); // one comment is from bot
|
assertThat(thread.getComments()).hasSize(2); // one comment is from bot
|
||||||
assertThat(thread.getComments().get(0).getBody()).isEqualTo(makePlainTextComment("comment one").getBody());
|
assertThat(thread.getComments().get(0).getBody()).isEqualTo(makePlainTextComment("comment one").getBody());
|
||||||
assertThat(thread.getComments().get(0).getPolicies()).containsExactlyInAnyOrder(
|
assertThat(thread.getComments().get(0).getPolicies()).containsExactlyInAnyOrder(
|
||||||
Policy.builder().permission(AclPermission.MANAGE_COMMENT.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build(),
|
Policy.builder().permission(AclPermission.MANAGE_COMMENTS.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build(),
|
||||||
Policy.builder().permission(AclPermission.READ_COMMENT.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build()
|
Policy.builder().permission(AclPermission.READ_COMMENTS.getValue()).users(Set.of("api_user")).groups(Collections.emptySet()).build()
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThat(threadsInApp).hasSize(1);
|
assertThat(threadsInApp).hasSize(1);
|
||||||
|
|
@ -325,7 +325,7 @@ public class CommentServiceTest {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setEmail("api_user");
|
user.setEmail("api_user");
|
||||||
Map<String, Policy> stringPolicyMap = policyUtils.generatePolicyFromPermission(
|
Map<String, Policy> stringPolicyMap = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.READ_THREAD),
|
Set.of(AclPermission.READ_THREADS),
|
||||||
user
|
user
|
||||||
);
|
);
|
||||||
Set<Policy> policies = Set.copyOf(stringPolicyMap.values());
|
Set<Policy> policies = Set.copyOf(stringPolicyMap.values());
|
||||||
|
|
@ -369,7 +369,7 @@ public class CommentServiceTest {
|
||||||
public void create_WhenThreadIsResolvedAndAlreadyViewed_ThreadIsUnresolvedAndUnread() {
|
public void create_WhenThreadIsResolvedAndAlreadyViewed_ThreadIsUnresolvedAndUnread() {
|
||||||
// create a thread first with resolved=true
|
// create a thread first with resolved=true
|
||||||
Collection<Policy> threadPolicies = policyUtils.generatePolicyFromPermission(
|
Collection<Policy> threadPolicies = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.COMMENT_ON_THREAD),
|
Set.of(AclPermission.COMMENT_ON_THREADS),
|
||||||
"api_user"
|
"api_user"
|
||||||
).values();
|
).values();
|
||||||
|
|
||||||
|
|
@ -587,7 +587,7 @@ public class CommentServiceTest {
|
||||||
|
|
||||||
// create a thread first with resolved=true
|
// create a thread first with resolved=true
|
||||||
Collection<Policy> threadPolicies = policyUtils.generatePolicyFromPermission(
|
Collection<Policy> threadPolicies = policyUtils.generatePolicyFromPermission(
|
||||||
Set.of(AclPermission.COMMENT_ON_THREAD),
|
Set.of(AclPermission.COMMENT_ON_THREADS),
|
||||||
"api_user"
|
"api_user"
|
||||||
).values();
|
).values();
|
||||||
CommentThread commentThread = new CommentThread();
|
CommentThread commentThread = new CommentThread();
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@ package com.appsmith.server.services;
|
||||||
|
|
||||||
import com.appsmith.external.models.Policy;
|
import com.appsmith.external.models.Policy;
|
||||||
import com.appsmith.server.acl.AclPermission;
|
import com.appsmith.server.acl.AclPermission;
|
||||||
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.ApplicationMode;
|
import com.appsmith.server.domains.ApplicationMode;
|
||||||
import com.appsmith.server.domains.Theme;
|
import com.appsmith.server.domains.Theme;
|
||||||
|
import com.appsmith.server.dtos.ApplicationAccessDTO;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.helpers.PolicyUtils;
|
import com.appsmith.server.helpers.PolicyUtils;
|
||||||
import com.appsmith.server.repositories.ApplicationRepository;
|
import com.appsmith.server.repositories.ApplicationRepository;
|
||||||
import com.appsmith.server.repositories.ThemeRepository;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -19,12 +21,17 @@ import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
import reactor.util.function.Tuple2;
|
import reactor.util.function.Tuple2;
|
||||||
import reactor.util.function.Tuple3;
|
import reactor.util.function.Tuple3;
|
||||||
|
import reactor.util.function.Tuples;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static com.appsmith.server.acl.AclPermission.MAKE_PUBLIC_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.MANAGE_THEMES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
|
|
@ -38,7 +45,7 @@ public class ThemeServiceTest {
|
||||||
ApplicationRepository applicationRepository;
|
ApplicationRepository applicationRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
ThemeRepository themeRepository;
|
ApplicationService applicationService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ThemeService themeService;
|
private ThemeService themeService;
|
||||||
|
|
@ -55,20 +62,20 @@ public class ThemeServiceTest {
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void getApplicationTheme_WhenThemeIsSet_ThemesReturned() {
|
public void getApplicationTheme_WhenThemeIsSet_ThemesReturned() {
|
||||||
Mono<Tuple2<Theme, Theme>> applicationThemesMono = themeRepository.getSystemThemeByName("Classic")
|
Mono<Tuple2<Theme, Theme>> applicationThemesMono = themeService.getSystemTheme("Classic")
|
||||||
.zipWith(themeRepository.getSystemThemeByName("Sharp"))
|
.zipWith(themeService.getSystemTheme("Sharp"))
|
||||||
.flatMap(themesTuple -> {
|
.flatMap(themesTuple -> {
|
||||||
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(themesTuple.getT1().getId());
|
application.setEditModeThemeId(themesTuple.getT1().getId());
|
||||||
application.setPublishedModeThemeId(themesTuple.getT2().getId());
|
application.setPublishedModeThemeId(themesTuple.getT2().getId());
|
||||||
return applicationRepository.save(application);
|
return applicationRepository.save(application);
|
||||||
})
|
})
|
||||||
.flatMap(application -> {
|
.flatMap(application ->
|
||||||
return Mono.zip(
|
Mono.zip(
|
||||||
themeService.getApplicationTheme(application.getId(), ApplicationMode.EDIT),
|
themeService.getApplicationTheme(application.getId(), ApplicationMode.EDIT),
|
||||||
themeService.getApplicationTheme(application.getId(), ApplicationMode.PUBLISHED)
|
themeService.getApplicationTheme(application.getId(), ApplicationMode.PUBLISHED)
|
||||||
);
|
)
|
||||||
});
|
);
|
||||||
|
|
||||||
StepVerifier.create(applicationThemesMono)
|
StepVerifier.create(applicationThemesMono)
|
||||||
.assertNext(themesTuple -> {
|
.assertNext(themesTuple -> {
|
||||||
|
|
@ -82,20 +89,18 @@ public class ThemeServiceTest {
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void getApplicationTheme_WhenUserHasNoPermission_ExceptionThrows() {
|
public void getApplicationTheme_WhenUserHasNoPermission_ExceptionThrows() {
|
||||||
Mono<Tuple2<Theme, Theme>> applicationThemesMono = themeRepository.getSystemThemeByName("Classic")
|
Mono<Tuple2<Theme, Theme>> applicationThemesMono = themeService.getSystemTheme("Classic")
|
||||||
.zipWith(themeRepository.getSystemThemeByName("Sharp"))
|
.zipWith(themeService.getSystemTheme("Sharp"))
|
||||||
.flatMap(themesTuple -> {
|
.flatMap(themesTuple -> {
|
||||||
Application application = createApplication("random_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("random_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(themesTuple.getT1().getId());
|
application.setEditModeThemeId(themesTuple.getT1().getId());
|
||||||
application.setPublishedModeThemeId(themesTuple.getT2().getId());
|
application.setPublishedModeThemeId(themesTuple.getT2().getId());
|
||||||
return applicationRepository.save(application);
|
return applicationRepository.save(application);
|
||||||
})
|
})
|
||||||
.flatMap(application -> {
|
.flatMap(application -> Mono.zip(
|
||||||
return Mono.zip(
|
themeService.getApplicationTheme(application.getId(), ApplicationMode.EDIT),
|
||||||
themeService.getApplicationTheme(application.getId(), ApplicationMode.EDIT),
|
themeService.getApplicationTheme(application.getId(), ApplicationMode.PUBLISHED)
|
||||||
themeService.getApplicationTheme(application.getId(), ApplicationMode.PUBLISHED)
|
));
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
StepVerifier.create(applicationThemesMono)
|
StepVerifier.create(applicationThemesMono)
|
||||||
.expectError(AppsmithException.class)
|
.expectError(AppsmithException.class)
|
||||||
|
|
@ -105,7 +110,7 @@ public class ThemeServiceTest {
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void changeCurrentTheme_WhenUserHasPermission_ThemesSetInEditMode() {
|
public void changeCurrentTheme_WhenUserHasPermission_ThemesSetInEditMode() {
|
||||||
Mono<Tuple2<Application, Application>> tuple2Mono = themeRepository.getSystemThemeByName("Classic")
|
Mono<Tuple2<Application, Application>> tuple2Mono = themeService.getSystemTheme("Classic")
|
||||||
.flatMap(theme -> {
|
.flatMap(theme -> {
|
||||||
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(theme.getId());
|
application.setEditModeThemeId(theme.getId());
|
||||||
|
|
@ -113,7 +118,7 @@ public class ThemeServiceTest {
|
||||||
// setting classic theme to edit mode and published mode
|
// setting classic theme to edit mode and published mode
|
||||||
return applicationRepository.save(application);
|
return applicationRepository.save(application);
|
||||||
})
|
})
|
||||||
.zipWith(themeRepository.getSystemThemeByName("Rounded"))
|
.zipWith(themeService.getSystemTheme("Rounded"))
|
||||||
.flatMap(tuple -> {
|
.flatMap(tuple -> {
|
||||||
Application application = tuple.getT1();
|
Application application = tuple.getT1();
|
||||||
Theme theme = tuple.getT2();
|
Theme theme = tuple.getT2();
|
||||||
|
|
@ -141,7 +146,7 @@ public class ThemeServiceTest {
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void changeCurrentTheme_WhenUserHasNoPermission_ThrowsException() {
|
public void changeCurrentTheme_WhenUserHasNoPermission_ThrowsException() {
|
||||||
Mono<Theme> themeMono = themeRepository.getSystemThemeByName("Classic")
|
Mono<Theme> themeMono = themeService.getSystemTheme("Classic")
|
||||||
.flatMap(theme -> {
|
.flatMap(theme -> {
|
||||||
Application application = createApplication("some_other_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("some_other_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(theme.getId());
|
application.setEditModeThemeId(theme.getId());
|
||||||
|
|
@ -157,16 +162,84 @@ public class ThemeServiceTest {
|
||||||
StepVerifier.create(themeMono).expectError(AppsmithException.class).verify();
|
StepVerifier.create(themeMono).expectError(AppsmithException.class).verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void changeCurrentTheme_WhenSystemThemeSet_NoNewThemeCreated() {
|
||||||
|
Mono<String> defaultThemeIdMono = themeService.getDefaultThemeId().cache();
|
||||||
|
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
application.setOrganizationId("theme-test-org-id");
|
||||||
|
Mono<Theme> applicationThemeMono = defaultThemeIdMono
|
||||||
|
.flatMap(defaultThemeId -> {
|
||||||
|
application.setEditModeThemeId(defaultThemeId);
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
})
|
||||||
|
.flatMap(savedApplication ->
|
||||||
|
defaultThemeIdMono.flatMap(themeId ->
|
||||||
|
themeService.changeCurrentTheme(themeId, savedApplication.getId())
|
||||||
|
.then(themeService.getApplicationTheme(savedApplication.getId(), ApplicationMode.EDIT))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
StepVerifier.create(applicationThemeMono).assertNext(theme -> {
|
||||||
|
assertThat(theme.isSystemTheme()).isTrue();
|
||||||
|
assertThat(theme.getApplicationId()).isNull();
|
||||||
|
assertThat(theme.getOrganizationId()).isNull();
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void changeCurrentTheme_WhenSystemThemeSetOverCustomTheme_NewThemeNotCreatedAndOldOneDeleted() {
|
||||||
|
Collection<Policy> themePolicies = policyUtils.generatePolicyFromPermission(
|
||||||
|
Set.of(MANAGE_THEMES), "api_user"
|
||||||
|
).values();
|
||||||
|
|
||||||
|
Theme customTheme = new Theme();
|
||||||
|
customTheme.setName("my-custom-theme");
|
||||||
|
customTheme.setPolicies(Set.copyOf(themePolicies));
|
||||||
|
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
application.setOrganizationId("theme-test-org-id");
|
||||||
|
|
||||||
|
Mono<Tuple2<Theme, Theme>> tuple2Mono = themeService.save(customTheme)
|
||||||
|
.flatMap(savedTheme -> {
|
||||||
|
application.setEditModeThemeId(savedTheme.getId());
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
})
|
||||||
|
.flatMap(savedApplication ->
|
||||||
|
themeService.getDefaultThemeId()
|
||||||
|
.flatMap(themeId -> themeService.changeCurrentTheme(themeId, savedApplication.getId()))
|
||||||
|
.thenReturn(savedApplication)
|
||||||
|
).flatMap(application1 ->
|
||||||
|
// get old theme and new
|
||||||
|
Mono.zip(
|
||||||
|
themeService.getApplicationTheme(application1.getId(), ApplicationMode.EDIT),
|
||||||
|
themeService.getThemeById(application1.getEditModeThemeId(), READ_THEMES)
|
||||||
|
.defaultIfEmpty(new Theme()) // this should be deleted, return empty theme
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
StepVerifier.create(tuple2Mono).assertNext(themeTuple2 -> {
|
||||||
|
Theme currentTheme = themeTuple2.getT1();
|
||||||
|
Theme oldTheme = themeTuple2.getT2();
|
||||||
|
assertThat(currentTheme.isSystemTheme()).isTrue();
|
||||||
|
assertThat(currentTheme.getApplicationId()).isNull();
|
||||||
|
assertThat(currentTheme.getOrganizationId()).isNull();
|
||||||
|
assertThat(oldTheme.getId()).isNull();
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void cloneThemeToApplication_WhenSrcThemeIsSystemTheme_NoNewThemeCreated() {
|
public void cloneThemeToApplication_WhenSrcThemeIsSystemTheme_NoNewThemeCreated() {
|
||||||
Application newApplication = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application newApplication = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
Mono<Tuple2<Theme, Theme>> newAndOldThemeMono = applicationRepository.save(newApplication)
|
Mono<Tuple2<Theme, Theme>> newAndOldThemeMono = applicationRepository.save(newApplication)
|
||||||
.zipWith(themeRepository.getSystemThemeByName("Classic"))
|
.zipWith(themeService.getSystemTheme("Classic"))
|
||||||
.flatMap(applicationAndTheme -> {
|
.flatMap(applicationAndTheme -> {
|
||||||
Theme theme = applicationAndTheme.getT2();
|
Theme theme = applicationAndTheme.getT2();
|
||||||
Application application = applicationAndTheme.getT1();
|
Application application = applicationAndTheme.getT1();
|
||||||
return themeService.cloneThemeToApplication(theme.getId(), application.getId()).zipWith(Mono.just(theme));
|
return themeService.cloneThemeToApplication(theme.getId(), application).zipWith(Mono.just(theme));
|
||||||
});
|
});
|
||||||
|
|
||||||
StepVerifier.create(newAndOldThemeMono)
|
StepVerifier.create(newAndOldThemeMono)
|
||||||
|
|
@ -182,13 +255,16 @@ public class ThemeServiceTest {
|
||||||
Application newApplication = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application newApplication = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
Theme customTheme = new Theme();
|
Theme customTheme = new Theme();
|
||||||
customTheme.setName("custom theme");
|
customTheme.setName("custom theme");
|
||||||
|
customTheme.setPolicies(Set.copyOf(
|
||||||
|
policyUtils.generatePolicyFromPermission(Set.of(MANAGE_THEMES), "api_user").values()
|
||||||
|
));
|
||||||
|
|
||||||
Mono<Tuple2<Theme, Theme>> newAndOldThemeMono = applicationRepository.save(newApplication)
|
Mono<Tuple2<Theme, Theme>> newAndOldThemeMono = applicationRepository.save(newApplication)
|
||||||
.zipWith(themeRepository.save(customTheme))
|
.zipWith(themeService.save(customTheme))
|
||||||
.flatMap(applicationAndTheme -> {
|
.flatMap(applicationAndTheme -> {
|
||||||
Theme theme = applicationAndTheme.getT2();
|
Theme theme = applicationAndTheme.getT2();
|
||||||
Application application = applicationAndTheme.getT1();
|
Application application = applicationAndTheme.getT1();
|
||||||
return themeService.cloneThemeToApplication(theme.getId(), application.getId()).zipWith(Mono.just(theme));
|
return themeService.cloneThemeToApplication(theme.getId(), application).zipWith(Mono.just(theme));
|
||||||
});
|
});
|
||||||
|
|
||||||
StepVerifier.create(newAndOldThemeMono)
|
StepVerifier.create(newAndOldThemeMono)
|
||||||
|
|
@ -199,14 +275,57 @@ public class ThemeServiceTest {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void cloneThemeToApplication_WhenSrcThemeIsCustomSavedTheme_NewCustomThemeCreated() {
|
||||||
|
Application srcApplication = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
|
||||||
|
Mono<Tuple2<Theme, Theme>> newAndOldThemeMono = applicationRepository.save(srcApplication)
|
||||||
|
.flatMap(application -> {
|
||||||
|
Theme srcCustomTheme = new Theme();
|
||||||
|
srcCustomTheme.setName("custom theme");
|
||||||
|
srcCustomTheme.setApplicationId(application.getId());
|
||||||
|
srcCustomTheme.setPolicies(Set.copyOf(
|
||||||
|
policyUtils.generatePolicyFromPermission(Set.of(MANAGE_THEMES), "api_user").values()
|
||||||
|
));
|
||||||
|
return themeService.save(srcCustomTheme);
|
||||||
|
})
|
||||||
|
.zipWith(applicationRepository.save(createApplication("api_user", Set.of(MANAGE_APPLICATIONS))))
|
||||||
|
.flatMap(objects -> {
|
||||||
|
Theme srcTheme = objects.getT1();
|
||||||
|
Application destApp = objects.getT2();
|
||||||
|
return Mono.zip(
|
||||||
|
themeService.cloneThemeToApplication(srcTheme.getId(), destApp),
|
||||||
|
Mono.just(srcTheme)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
StepVerifier.create(newAndOldThemeMono)
|
||||||
|
.assertNext(objects -> {
|
||||||
|
Theme clonnedTheme = objects.getT1();
|
||||||
|
Theme srcTheme = objects.getT2();
|
||||||
|
|
||||||
|
assertThat(clonnedTheme.getId()).isNotEqualTo(srcTheme.getId());
|
||||||
|
assertThat(clonnedTheme.getName()).isEqualTo(srcTheme.getName());
|
||||||
|
assertThat(clonnedTheme.getApplicationId()).isNull();
|
||||||
|
assertThat(clonnedTheme.getOrganizationId()).isNull();
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void getApplicationTheme_WhenUserHasPermission_ThemeReturned() {
|
public void getApplicationTheme_WhenUserHasPermission_ThemeReturned() {
|
||||||
|
Collection<Policy> themePolicies = policyUtils.generatePolicyFromPermission(
|
||||||
|
Set.of(MANAGE_THEMES), "api_user"
|
||||||
|
).values();
|
||||||
|
|
||||||
Theme customTheme = new Theme();
|
Theme customTheme = new Theme();
|
||||||
customTheme.setName("custom theme for edit mode");
|
customTheme.setName("custom theme for edit mode");
|
||||||
|
customTheme.setPolicies(Set.copyOf(themePolicies));
|
||||||
|
|
||||||
Mono<Tuple2<Theme, Theme>> applicationThemesMono = themeRepository.save(customTheme)
|
Mono<Tuple2<Theme, Theme>> applicationThemesMono = themeService.save(customTheme)
|
||||||
.zipWith(themeRepository.getSystemThemeByName("classic"))
|
.zipWith(themeService.getSystemTheme("classic"))
|
||||||
.flatMap(themes -> {
|
.flatMap(themes -> {
|
||||||
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(themes.getT1().getId());
|
application.setEditModeThemeId(themes.getT1().getId());
|
||||||
|
|
@ -230,7 +349,7 @@ public class ThemeServiceTest {
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void publishTheme_WhenSystemThemeIsSet_NoNewThemeCreated() {
|
public void publishTheme_WhenSystemThemeIsSet_NoNewThemeCreated() {
|
||||||
Mono<Theme> classicThemeMono = themeRepository.getSystemThemeByName("classic").cache();
|
Mono<Theme> classicThemeMono = themeService.getSystemTheme("classic").cache();
|
||||||
|
|
||||||
Mono<Tuple2<Application, Theme>> appAndThemeTuple = classicThemeMono
|
Mono<Tuple2<Application, Theme>> appAndThemeTuple = classicThemeMono
|
||||||
.flatMap(theme -> {
|
.flatMap(theme -> {
|
||||||
|
|
@ -239,9 +358,8 @@ public class ThemeServiceTest {
|
||||||
application.setPublishedModeThemeId("this-id-should-be-overridden");
|
application.setPublishedModeThemeId("this-id-should-be-overridden");
|
||||||
return applicationRepository.save(application);
|
return applicationRepository.save(application);
|
||||||
}).flatMap(savedApplication ->
|
}).flatMap(savedApplication ->
|
||||||
themeService.publishTheme(savedApplication.getEditModeThemeId(),
|
themeService.publishTheme(savedApplication.getId())
|
||||||
savedApplication.getPublishedModeThemeId(), savedApplication.getId()
|
.then(applicationRepository.findById(savedApplication.getId()))
|
||||||
).then(applicationRepository.findById(savedApplication.getId()))
|
|
||||||
)
|
)
|
||||||
.zipWith(classicThemeMono);
|
.zipWith(classicThemeMono);
|
||||||
|
|
||||||
|
|
@ -254,23 +372,53 @@ public class ThemeServiceTest {
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void publishTheme_WhenSystemThemeInEditModeAndCustomThemeInPublishedMode_PublisedCopyDeleted() {
|
||||||
|
Mono<Theme> classicThemeMono = themeService.getSystemTheme("classic").cache();
|
||||||
|
|
||||||
|
Theme customTheme = new Theme();
|
||||||
|
customTheme.setName("published-theme-copy");
|
||||||
|
Mono<Theme> publishedCustomThemeMono = themeService.save(customTheme);
|
||||||
|
|
||||||
|
Mono<Theme> deletedThemeMono = classicThemeMono
|
||||||
|
.zipWith(publishedCustomThemeMono)
|
||||||
|
.flatMap(themesTuple -> {
|
||||||
|
Theme systemTheme = themesTuple.getT1();
|
||||||
|
Theme savedCustomTheme = themesTuple.getT2();
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
application.setEditModeThemeId(systemTheme.getId());
|
||||||
|
application.setPublishedModeThemeId(savedCustomTheme.getId());
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
}).flatMap(savedApplication ->
|
||||||
|
themeService.publishTheme(savedApplication.getId())
|
||||||
|
.then(themeService.getThemeById(savedApplication.getPublishedModeThemeId(), READ_THEMES))
|
||||||
|
);
|
||||||
|
|
||||||
|
StepVerifier.create(deletedThemeMono)
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void publishTheme_WhenCustomThemeIsSet_ThemeCopiedForPublishedMode() {
|
public void publishTheme_WhenCustomThemeIsSet_ThemeCopiedForPublishedMode() {
|
||||||
|
Collection<Policy> themePolicies = policyUtils.generatePolicyFromPermission(
|
||||||
|
Set.of(MANAGE_THEMES), "api_user"
|
||||||
|
).values();
|
||||||
|
|
||||||
Theme customTheme = new Theme();
|
Theme customTheme = new Theme();
|
||||||
customTheme.setName("my-custom-theme");
|
customTheme.setName("my-custom-theme");
|
||||||
|
customTheme.setPolicies(Set.copyOf(themePolicies));
|
||||||
|
|
||||||
Mono<Tuple2<Theme, Theme>> appThemesMono = themeRepository.save(customTheme)
|
Mono<Tuple2<Theme, Theme>> appThemesMono = themeService.save(customTheme)
|
||||||
.zipWith(themeRepository.getSystemThemeByName("classic"))
|
.zipWith(themeService.getSystemTheme("classic"))
|
||||||
.flatMap(themes -> {
|
.flatMap(themes -> {
|
||||||
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(themes.getT1().getId()); // custom theme
|
application.setEditModeThemeId(themes.getT1().getId()); // custom theme
|
||||||
application.setPublishedModeThemeId(themes.getT2().getId()); // system theme
|
application.setPublishedModeThemeId(themes.getT2().getId()); // system theme
|
||||||
return applicationRepository.save(application);
|
return applicationRepository.save(application);
|
||||||
}).flatMap(application ->
|
}).flatMap(application ->
|
||||||
themeService.publishTheme(application.getEditModeThemeId(),
|
themeService.publishTheme(application.getId()).then(Mono.zip(
|
||||||
application.getPublishedModeThemeId(), application.getId()
|
|
||||||
).then(Mono.zip(
|
|
||||||
themeService.getApplicationTheme(application.getId(), ApplicationMode.EDIT),
|
themeService.getApplicationTheme(application.getId(), ApplicationMode.EDIT),
|
||||||
themeService.getApplicationTheme(application.getId(), ApplicationMode.PUBLISHED)
|
themeService.getApplicationTheme(application.getId(), ApplicationMode.PUBLISHED)
|
||||||
))
|
))
|
||||||
|
|
@ -291,7 +439,7 @@ public class ThemeServiceTest {
|
||||||
Theme customTheme = new Theme();
|
Theme customTheme = new Theme();
|
||||||
customTheme.setName("My custom theme");
|
customTheme.setName("My custom theme");
|
||||||
|
|
||||||
Mono<Tuple2<Theme, Theme>> appThemesMono = themeRepository.getSystemThemeByName("classic")
|
Mono<Tuple2<Theme, Theme>> appThemesMono = themeService.getSystemTheme("classic")
|
||||||
.flatMap(theme -> {
|
.flatMap(theme -> {
|
||||||
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(theme.getId()); // system theme
|
application.setEditModeThemeId(theme.getId()); // system theme
|
||||||
|
|
@ -319,12 +467,17 @@ public class ThemeServiceTest {
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void updateTheme_WhenCustomThemeIsSet_ThemeIsOverridden() {
|
public void updateTheme_WhenCustomThemeIsSet_ThemeIsOverridden() {
|
||||||
|
Collection<Policy> themePolicies = policyUtils.generatePolicyFromPermission(
|
||||||
|
Set.of(MANAGE_THEMES), "api_user"
|
||||||
|
).values();
|
||||||
Theme customTheme = new Theme();
|
Theme customTheme = new Theme();
|
||||||
customTheme.setName("My custom theme");
|
customTheme.setName("My custom theme");
|
||||||
Mono<Theme> saveCustomThemeMono = themeRepository.save(customTheme);
|
customTheme.setPolicies(Set.copyOf(themePolicies));
|
||||||
|
|
||||||
|
Mono<Theme> saveCustomThemeMono = themeService.save(customTheme);
|
||||||
|
|
||||||
Mono<Tuple3<Theme, Theme, Application>> appThemesMono = saveCustomThemeMono
|
Mono<Tuple3<Theme, Theme, Application>> appThemesMono = saveCustomThemeMono
|
||||||
.zipWith(themeRepository.getSystemThemeByName("classic"))
|
.zipWith(themeService.getSystemTheme("classic"))
|
||||||
.flatMap(themes -> {
|
.flatMap(themes -> {
|
||||||
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
application.setEditModeThemeId(themes.getT1().getId()); // custom theme
|
application.setEditModeThemeId(themes.getT1().getId()); // custom theme
|
||||||
|
|
@ -361,15 +514,14 @@ public class ThemeServiceTest {
|
||||||
@WithUserDetails("api_user")
|
@WithUserDetails("api_user")
|
||||||
@Test
|
@Test
|
||||||
public void publishTheme_WhenNoThemeIsSet_SystemDefaultThemeIsSetToPublishedMode() {
|
public void publishTheme_WhenNoThemeIsSet_SystemDefaultThemeIsSetToPublishedMode() {
|
||||||
Mono<Theme> classicThemeMono = themeRepository.getSystemThemeByName(Theme.LEGACY_THEME_NAME);
|
Mono<Theme> classicThemeMono = themeService.getSystemTheme(Theme.LEGACY_THEME_NAME);
|
||||||
|
|
||||||
Mono<Tuple2<Application, Theme>> appAndThemeTuple = applicationRepository.save(
|
Mono<Tuple2<Application, Theme>> appAndThemeTuple = applicationRepository.save(
|
||||||
createApplication("api_user", Set.of(MANAGE_APPLICATIONS))
|
createApplication("api_user", Set.of(MANAGE_APPLICATIONS))
|
||||||
)
|
)
|
||||||
.flatMap(savedApplication ->
|
.flatMap(savedApplication ->
|
||||||
themeService.publishTheme(savedApplication.getEditModeThemeId(),
|
themeService.publishTheme(savedApplication.getId())
|
||||||
savedApplication.getPublishedModeThemeId(), savedApplication.getId()
|
.then(applicationRepository.findById(savedApplication.getId()))
|
||||||
).then(applicationRepository.findById(savedApplication.getId()))
|
|
||||||
)
|
)
|
||||||
.zipWith(classicThemeMono);
|
.zipWith(classicThemeMono);
|
||||||
|
|
||||||
|
|
@ -380,4 +532,214 @@ public class ThemeServiceTest {
|
||||||
assertThat(application.getPublishedModeThemeId()).isEqualTo(classicSystemTheme.getId());
|
assertThat(application.getPublishedModeThemeId()).isEqualTo(classicSystemTheme.getId());
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void publishTheme_WhenApplicationIsPublic_PublishedThemeIsPublic() {
|
||||||
|
Collection<Policy> themePolicies = policyUtils.generatePolicyFromPermission(
|
||||||
|
Set.of(MANAGE_THEMES), "api_user"
|
||||||
|
).values();
|
||||||
|
|
||||||
|
Theme customTheme = new Theme();
|
||||||
|
customTheme.setName("my-custom-theme");
|
||||||
|
customTheme.setPolicies(Set.copyOf(themePolicies));
|
||||||
|
|
||||||
|
Mono<Theme> appThemesMono = themeService.save(customTheme)
|
||||||
|
.zipWith(themeService.getSystemTheme("classic"))
|
||||||
|
.flatMap(themes -> {
|
||||||
|
Application application = createApplication("api_user",
|
||||||
|
Set.of(MAKE_PUBLIC_APPLICATIONS, MANAGE_APPLICATIONS));
|
||||||
|
application.setEditModeThemeId(themes.getT1().getId()); // custom theme
|
||||||
|
application.setPublishedModeThemeId(themes.getT2().getId()); // system theme
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
})
|
||||||
|
.flatMap(application -> {
|
||||||
|
// make the application public
|
||||||
|
ApplicationAccessDTO accessDTO = new ApplicationAccessDTO();
|
||||||
|
accessDTO.setPublicAccess(true);
|
||||||
|
return applicationService.changeViewAccess(application.getId(), accessDTO);
|
||||||
|
})
|
||||||
|
.flatMap(application ->
|
||||||
|
themeService.publishTheme(application.getId()).then(
|
||||||
|
themeService.getApplicationTheme(application.getId(), ApplicationMode.PUBLISHED)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
StepVerifier.create(appThemesMono)
|
||||||
|
.assertNext(publishedModeTheme -> {
|
||||||
|
Boolean permissionPresentForAnonymousUser = policyUtils.isPermissionPresentForUser(
|
||||||
|
publishedModeTheme.getPolicies(), READ_THEMES.getValue(), FieldName.ANONYMOUS_USER
|
||||||
|
);
|
||||||
|
assertThat(permissionPresentForAnonymousUser).isTrue();
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void persistCurrentTheme_WhenCustomThemeIsSet_NewApplicationThemeCreated() {
|
||||||
|
Collection<Policy> themePolicies = policyUtils.generatePolicyFromPermission(
|
||||||
|
Set.of(MANAGE_THEMES), "api_user"
|
||||||
|
).values();
|
||||||
|
|
||||||
|
Theme customTheme = new Theme();
|
||||||
|
customTheme.setName("Classic");
|
||||||
|
customTheme.setPolicies(Set.copyOf(themePolicies));
|
||||||
|
|
||||||
|
Mono<Tuple3<List<Theme>, Theme, Application>> tuple3Mono = themeService.save(customTheme).flatMap(theme -> {
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
application.setEditModeThemeId(theme.getId());
|
||||||
|
application.setOrganizationId("theme-test-org-id");
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
}).flatMap(application -> {
|
||||||
|
Theme theme = new Theme();
|
||||||
|
theme.setName("My custom theme");
|
||||||
|
return themeService.persistCurrentTheme(application.getId(), theme)
|
||||||
|
.map(theme1 -> Tuples.of(theme1, application));
|
||||||
|
}).flatMap(persistedThemeAndApp ->
|
||||||
|
themeService.getApplicationThemes(persistedThemeAndApp.getT2().getId()).collectList()
|
||||||
|
.map(themes -> Tuples.of(themes, persistedThemeAndApp.getT1(), persistedThemeAndApp.getT2()))
|
||||||
|
);
|
||||||
|
|
||||||
|
StepVerifier.create(tuple3Mono).assertNext(tuple3 -> {
|
||||||
|
List<Theme> availableThemes = tuple3.getT1();
|
||||||
|
Theme persistedTheme = tuple3.getT2();
|
||||||
|
Application application = tuple3.getT3();
|
||||||
|
assertThat(availableThemes.size()).isEqualTo(5); // one custom theme + 4 system themes
|
||||||
|
assertThat(persistedTheme.getApplicationId()).isNotEmpty(); // theme should have application id set
|
||||||
|
assertThat(persistedTheme.getOrganizationId()).isEqualTo("theme-test-org-id"); // theme should have org id set
|
||||||
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
|
persistedTheme.getPolicies(), READ_THEMES.getValue(), "api_user")
|
||||||
|
).isTrue();
|
||||||
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
|
persistedTheme.getPolicies(), MANAGE_THEMES.getValue(), "api_user")
|
||||||
|
).isTrue();
|
||||||
|
assertThat(application.getEditModeThemeId()).isNotEqualTo(persistedTheme.getId()); // a new copy should be created
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void persistCurrentTheme_WhenSystemThemeIsSet_NewApplicationThemeCreated() {
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
application.setOrganizationId("theme-test-org-id");
|
||||||
|
Mono<Tuple2<List<Theme>, Theme>> tuple2Mono = themeService.getDefaultThemeId()
|
||||||
|
.flatMap(defaultThemeId -> {
|
||||||
|
application.setEditModeThemeId(defaultThemeId);
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
})
|
||||||
|
.flatMap(savedApplication -> {
|
||||||
|
Theme theme = new Theme();
|
||||||
|
theme.setName("My custom theme");
|
||||||
|
return themeService.persistCurrentTheme(savedApplication.getId(), theme)
|
||||||
|
.map(theme1 -> Tuples.of(theme1, savedApplication.getId()));
|
||||||
|
}).flatMap(persistedThemeAndAppId ->
|
||||||
|
themeService.getApplicationThemes(persistedThemeAndAppId.getT2()).collectList()
|
||||||
|
.map(themes -> Tuples.of(themes, persistedThemeAndAppId.getT1()))
|
||||||
|
);
|
||||||
|
|
||||||
|
StepVerifier.create(tuple2Mono).assertNext(tuple2 -> {
|
||||||
|
List<Theme> availableThemes = tuple2.getT1();
|
||||||
|
Theme currentTheme = tuple2.getT2();
|
||||||
|
assertThat(availableThemes.size()).isEqualTo(5); // one custom theme + 4 system themes
|
||||||
|
assertThat(currentTheme.isSystemTheme()).isFalse();
|
||||||
|
assertThat(currentTheme.getApplicationId()).isNotEmpty(); // theme should have application id set
|
||||||
|
assertThat(currentTheme.getOrganizationId()).isEqualTo("theme-test-org-id"); // theme should have org id set
|
||||||
|
assertThat(policyUtils.isPermissionPresentForUser(currentTheme.getPolicies(), READ_THEMES.getValue(), "api_user")).isTrue();
|
||||||
|
assertThat(policyUtils.isPermissionPresentForUser(currentTheme.getPolicies(), MANAGE_THEMES.getValue(), "api_user")).isTrue();
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void delete_WhenSystemTheme_NotAllowed() {
|
||||||
|
StepVerifier.create(themeService.getDefaultThemeId().flatMap(themeService::delete))
|
||||||
|
.expectError(AppsmithException.class)
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void delete_WhenUnsavedCustomizedTheme_NotAllowed() {
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
|
||||||
|
Mono<Theme> deleteThemeMono = themeService.getDefaultThemeId()
|
||||||
|
.flatMap(s -> {
|
||||||
|
application.setEditModeThemeId(s);
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
})
|
||||||
|
.flatMap(savedApplication -> {
|
||||||
|
Theme themeCustomization = new Theme();
|
||||||
|
themeCustomization.setName("Updated name");
|
||||||
|
return themeService.updateTheme(savedApplication.getId(), themeCustomization);
|
||||||
|
}).flatMap(customizedTheme -> themeService.delete(customizedTheme.getId()));
|
||||||
|
|
||||||
|
StepVerifier.create(deleteThemeMono)
|
||||||
|
.expectErrorMessage(AppsmithError.UNSUPPORTED_OPERATION.getMessage())
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void delete_WhenSavedCustomizedTheme_ThemeIsDeleted() {
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
|
||||||
|
Mono<Theme> deleteThemeMono = themeService.getDefaultThemeId()
|
||||||
|
.flatMap(s -> {
|
||||||
|
application.setEditModeThemeId(s);
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
})
|
||||||
|
.flatMap(savedApplication -> {
|
||||||
|
Theme themeCustomization = new Theme();
|
||||||
|
themeCustomization.setName("Updated name");
|
||||||
|
return themeService.persistCurrentTheme(savedApplication.getId(), themeCustomization);
|
||||||
|
})
|
||||||
|
.flatMap(customizedTheme -> themeService.delete(customizedTheme.getId())
|
||||||
|
.then(themeService.getThemeById(customizedTheme.getId(), READ_THEMES)));
|
||||||
|
|
||||||
|
StepVerifier.create(deleteThemeMono).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void updateName_WhenSystemTheme_NotAllowed() {
|
||||||
|
Mono<Theme> updateThemeNameMono = themeService.getDefaultThemeId().flatMap(themeId -> {
|
||||||
|
Theme theme = new Theme();
|
||||||
|
theme.setName("My theme");
|
||||||
|
return themeService.updateName(themeId, theme);
|
||||||
|
});
|
||||||
|
StepVerifier.create(updateThemeNameMono).expectError(AppsmithException.class).verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
@Test
|
||||||
|
public void updateName_WhenCustomTheme_NameUpdated() {
|
||||||
|
Application application = createApplication("api_user", Set.of(MANAGE_APPLICATIONS));
|
||||||
|
application.setOrganizationId("test-org");
|
||||||
|
|
||||||
|
Mono<Theme> updateThemeNameMono = themeService.getDefaultThemeId()
|
||||||
|
.flatMap(s -> {
|
||||||
|
application.setEditModeThemeId(s);
|
||||||
|
return applicationRepository.save(application);
|
||||||
|
})
|
||||||
|
.flatMap(savedApplication -> {
|
||||||
|
Theme themeCustomization = new Theme();
|
||||||
|
themeCustomization.setName("old name");
|
||||||
|
return themeService.persistCurrentTheme(savedApplication.getId(), themeCustomization);
|
||||||
|
})
|
||||||
|
.flatMap(customizedTheme -> {
|
||||||
|
Theme theme = new Theme();
|
||||||
|
theme.setName("new name");
|
||||||
|
return themeService.updateName(customizedTheme.getId(), theme)
|
||||||
|
.then(themeService.getThemeById(customizedTheme.getId(), READ_THEMES));
|
||||||
|
});
|
||||||
|
|
||||||
|
StepVerifier.create(updateThemeNameMono).assertNext(theme -> {
|
||||||
|
assertThat(theme.getName()).isEqualTo("new name");
|
||||||
|
assertThat(theme.isSystemTheme()).isFalse();
|
||||||
|
assertThat(theme.getApplicationId()).isNotNull();
|
||||||
|
assertThat(theme.getOrganizationId()).isEqualTo("test-org");
|
||||||
|
assertThat(theme.getConfig()).isNotNull();
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -277,10 +277,10 @@ class UserOrganizationServiceTest {
|
||||||
StepVerifier.create(commentThreadMono).assertNext(commentThread -> {
|
StepVerifier.create(commentThreadMono).assertNext(commentThread -> {
|
||||||
Set<Policy> policies = commentThread.getPolicies();
|
Set<Policy> policies = commentThread.getPolicies();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
policies, AclPermission.READ_THREAD.getValue(), "test_developer"
|
policies, AclPermission.READ_THREADS.getValue(), "test_developer"
|
||||||
)).isTrue();
|
)).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
policies, AclPermission.READ_THREAD.getValue(), "api_user"
|
policies, AclPermission.READ_THREADS.getValue(), "api_user"
|
||||||
)).isTrue();
|
)).isTrue();
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
@ -308,10 +308,10 @@ class UserOrganizationServiceTest {
|
||||||
StepVerifier.create(commentThreadMono).assertNext(commentThread -> {
|
StepVerifier.create(commentThreadMono).assertNext(commentThread -> {
|
||||||
Set<Policy> policies = commentThread.getPolicies();
|
Set<Policy> policies = commentThread.getPolicies();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
policies, AclPermission.READ_THREAD.getValue(), "test_developer"
|
policies, AclPermission.READ_THREADS.getValue(), "test_developer"
|
||||||
)).isFalse();
|
)).isFalse();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
policies, AclPermission.READ_THREAD.getValue(), "api_user"
|
policies, AclPermission.READ_THREADS.getValue(), "api_user"
|
||||||
)).isTrue();
|
)).isTrue();
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
@ -346,13 +346,13 @@ class UserOrganizationServiceTest {
|
||||||
StepVerifier.create(saveUserMono.then(commentThreadMono)).assertNext(commentThread -> {
|
StepVerifier.create(saveUserMono.then(commentThreadMono)).assertNext(commentThread -> {
|
||||||
Set<Policy> policies = commentThread.getPolicies();
|
Set<Policy> policies = commentThread.getPolicies();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
policies, AclPermission.READ_THREAD.getValue(), "test_developer"
|
policies, AclPermission.READ_THREADS.getValue(), "test_developer"
|
||||||
)).isTrue();
|
)).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
policies, AclPermission.READ_THREAD.getValue(), "new_test_user"
|
policies, AclPermission.READ_THREADS.getValue(), "new_test_user"
|
||||||
)).isTrue();
|
)).isTrue();
|
||||||
assertThat(policyUtils.isPermissionPresentForUser(
|
assertThat(policyUtils.isPermissionPresentForUser(
|
||||||
policies, AclPermission.READ_THREAD.getValue(), "api_user"
|
policies, AclPermission.READ_THREADS.getValue(), "api_user"
|
||||||
)).isTrue();
|
)).isTrue();
|
||||||
}).verifyComplete();
|
}).verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,14 @@ import com.appsmith.server.acl.AppsmithRole;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
|
import com.appsmith.server.domains.ApplicationMode;
|
||||||
import com.appsmith.server.domains.Layout;
|
import com.appsmith.server.domains.Layout;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.domains.NewPage;
|
import com.appsmith.server.domains.NewPage;
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
import com.appsmith.server.domains.Plugin;
|
import com.appsmith.server.domains.Plugin;
|
||||||
import com.appsmith.server.domains.PluginType;
|
import com.appsmith.server.domains.PluginType;
|
||||||
|
import com.appsmith.server.domains.Theme;
|
||||||
import com.appsmith.server.dtos.ActionCollectionDTO;
|
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||||
import com.appsmith.server.dtos.ActionDTO;
|
import com.appsmith.server.dtos.ActionDTO;
|
||||||
import com.appsmith.server.dtos.InviteUsersDTO;
|
import com.appsmith.server.dtos.InviteUsersDTO;
|
||||||
|
|
@ -34,6 +36,7 @@ import com.appsmith.server.services.NewActionService;
|
||||||
import com.appsmith.server.services.NewPageService;
|
import com.appsmith.server.services.NewPageService;
|
||||||
import com.appsmith.server.services.OrganizationService;
|
import com.appsmith.server.services.OrganizationService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
|
import com.appsmith.server.services.ThemeService;
|
||||||
import com.appsmith.server.services.UserService;
|
import com.appsmith.server.services.UserService;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
@ -60,6 +63,8 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.test.StepVerifier;
|
import reactor.test.StepVerifier;
|
||||||
|
import reactor.util.function.Tuple3;
|
||||||
|
import reactor.util.function.Tuple4;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -67,6 +72,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
||||||
|
|
@ -137,6 +143,9 @@ public class ApplicationForkingServiceTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private LayoutCollectionService layoutCollectionService;
|
private LayoutCollectionService layoutCollectionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ThemeService themeService;
|
||||||
|
|
||||||
private static String sourceAppId;
|
private static String sourceAppId;
|
||||||
|
|
||||||
private static String testUserOrgId;
|
private static String testUserOrgId;
|
||||||
|
|
@ -484,6 +493,192 @@ public class ApplicationForkingServiceTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
public void forkApplicationToOrganization_WhenAppHasUnsavedThemeCustomization_ForkedWithCustomizations() {
|
||||||
|
String uniqueString = UUID.randomUUID().toString();
|
||||||
|
Organization organization = new Organization();
|
||||||
|
organization.setName("org_" + uniqueString);
|
||||||
|
|
||||||
|
Mono<Tuple4<Theme, Theme, Application, Application>> tuple4Mono = organizationService.create(organization)
|
||||||
|
.flatMap(createdOrg -> {
|
||||||
|
Application application = new Application();
|
||||||
|
application.setName("app_" + uniqueString);
|
||||||
|
return applicationPageService.createApplication(application, createdOrg.getId());
|
||||||
|
}).flatMap(srcApplication -> {
|
||||||
|
Theme theme = new Theme();
|
||||||
|
theme.setName("theme_" + uniqueString);
|
||||||
|
return themeService.updateTheme(srcApplication.getId(), theme)
|
||||||
|
.then(applicationService.findById(srcApplication.getId()));
|
||||||
|
}).flatMap(srcApplication -> {
|
||||||
|
Organization desOrg = new Organization();
|
||||||
|
desOrg.setName("org_dest_" + uniqueString);
|
||||||
|
return organizationService.create(desOrg).flatMap(createdOrg ->
|
||||||
|
applicationForkingService.forkApplicationToOrganization(srcApplication.getId(), createdOrg.getId())
|
||||||
|
).zipWith(Mono.just(srcApplication));
|
||||||
|
}).flatMap(applicationTuple2 -> {
|
||||||
|
Application forkedApp = applicationTuple2.getT1();
|
||||||
|
Application srcApp = applicationTuple2.getT2();
|
||||||
|
return Mono.zip(
|
||||||
|
themeService.getApplicationTheme(forkedApp.getId(), ApplicationMode.EDIT),
|
||||||
|
themeService.getApplicationTheme(forkedApp.getId(), ApplicationMode.PUBLISHED),
|
||||||
|
Mono.just(forkedApp),
|
||||||
|
Mono.just(srcApp)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
StepVerifier.create(tuple4Mono).assertNext(objects -> {
|
||||||
|
Theme editModeTheme = objects.getT1();
|
||||||
|
Theme publishedModeTheme = objects.getT2();
|
||||||
|
Application forkedApp = objects.getT3();
|
||||||
|
Application srcApp = objects.getT4();
|
||||||
|
|
||||||
|
assertThat(forkedApp.getEditModeThemeId()).isEqualTo(editModeTheme.getId());
|
||||||
|
assertThat(forkedApp.getPublishedModeThemeId()).isEqualTo(publishedModeTheme.getId());
|
||||||
|
assertThat(forkedApp.getEditModeThemeId()).isNotEqualTo(forkedApp.getPublishedModeThemeId());
|
||||||
|
|
||||||
|
// published mode should have the custom theme as we publish after forking the app
|
||||||
|
assertThat(publishedModeTheme.isSystemTheme()).isFalse();
|
||||||
|
// published mode theme will have no application id and org id set as the customizations were not saved
|
||||||
|
assertThat(publishedModeTheme.getOrganizationId()).isNullOrEmpty();
|
||||||
|
assertThat(publishedModeTheme.getApplicationId()).isNullOrEmpty();
|
||||||
|
|
||||||
|
// edit mode theme should be a custom one
|
||||||
|
assertThat(editModeTheme.isSystemTheme()).isFalse();
|
||||||
|
// edit mode theme will have no application id and org id set as the customizations were not saved
|
||||||
|
assertThat(editModeTheme.getOrganizationId()).isNullOrEmpty();
|
||||||
|
assertThat(editModeTheme.getApplicationId()).isNullOrEmpty();
|
||||||
|
|
||||||
|
// forked theme should have the same name as src theme
|
||||||
|
assertThat(editModeTheme.getName()).isEqualTo("theme_" + uniqueString);
|
||||||
|
assertThat(publishedModeTheme.getName()).isEqualTo("theme_" + uniqueString);
|
||||||
|
|
||||||
|
// forked application should have a new edit mode theme created, should not be same as src app theme
|
||||||
|
assertThat(srcApp.getEditModeThemeId()).isNotEqualTo(forkedApp.getEditModeThemeId());
|
||||||
|
assertThat(srcApp.getPublishedModeThemeId()).isNotEqualTo(forkedApp.getPublishedModeThemeId());
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
public void forkApplicationToOrganization_WhenAppHasSystemTheme_SystemThemeSet() {
|
||||||
|
String uniqueString = UUID.randomUUID().toString();
|
||||||
|
Organization organization = new Organization();
|
||||||
|
organization.setName("org_" + uniqueString);
|
||||||
|
|
||||||
|
Mono<Tuple3<Theme, Application, Application>> tuple3Mono = organizationService.create(organization)
|
||||||
|
.flatMap(createdOrg -> {
|
||||||
|
Application application = new Application();
|
||||||
|
application.setName("app_" + uniqueString);
|
||||||
|
return applicationPageService.createApplication(application, createdOrg.getId());
|
||||||
|
}).flatMap(srcApplication -> {
|
||||||
|
Organization desOrg = new Organization();
|
||||||
|
desOrg.setName("org_dest_" + uniqueString);
|
||||||
|
return organizationService.create(desOrg).flatMap(createdOrg ->
|
||||||
|
applicationForkingService.forkApplicationToOrganization(srcApplication.getId(), createdOrg.getId())
|
||||||
|
).zipWith(Mono.just(srcApplication));
|
||||||
|
}).flatMap(applicationTuple2 -> {
|
||||||
|
Application forkedApp = applicationTuple2.getT1();
|
||||||
|
Application srcApp = applicationTuple2.getT2();
|
||||||
|
return Mono.zip(
|
||||||
|
themeService.getApplicationTheme(forkedApp.getId(), ApplicationMode.EDIT),
|
||||||
|
Mono.just(forkedApp),
|
||||||
|
Mono.just(srcApp)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
StepVerifier.create(tuple3Mono).assertNext(objects -> {
|
||||||
|
Theme editModeTheme = objects.getT1();
|
||||||
|
Application forkedApp = objects.getT2();
|
||||||
|
Application srcApp = objects.getT3();
|
||||||
|
|
||||||
|
// same theme should be set to edit mode and published mode
|
||||||
|
assertThat(forkedApp.getEditModeThemeId()).isEqualTo(editModeTheme.getId());
|
||||||
|
assertThat(forkedApp.getPublishedModeThemeId()).isEqualTo(editModeTheme.getId());
|
||||||
|
|
||||||
|
// edit mode theme should be system theme
|
||||||
|
assertThat(editModeTheme.isSystemTheme()).isTrue();
|
||||||
|
// edit mode theme will have no application id and org id set as it's system theme
|
||||||
|
assertThat(editModeTheme.getOrganizationId()).isNullOrEmpty();
|
||||||
|
assertThat(editModeTheme.getApplicationId()).isNullOrEmpty();
|
||||||
|
|
||||||
|
// forked theme should be default theme
|
||||||
|
assertThat(editModeTheme.getName()).isEqualToIgnoringCase(Theme.DEFAULT_THEME_NAME);
|
||||||
|
|
||||||
|
// forked application should have same theme set
|
||||||
|
assertThat(srcApp.getEditModeThemeId()).isEqualTo(forkedApp.getEditModeThemeId());
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUserDetails("api_user")
|
||||||
|
public void forkApplicationToOrganization_WhenAppHasCustomSavedTheme_NewCustomThemeCreated() {
|
||||||
|
String uniqueString = UUID.randomUUID().toString();
|
||||||
|
Organization organization = new Organization();
|
||||||
|
organization.setName("org_" + uniqueString);
|
||||||
|
|
||||||
|
Mono<Tuple4<Theme, Theme, Application, Application>> tuple4Mono = organizationService.create(organization)
|
||||||
|
.flatMap(createdOrg -> {
|
||||||
|
Application application = new Application();
|
||||||
|
application.setName("app_" + uniqueString);
|
||||||
|
return applicationPageService.createApplication(application, createdOrg.getId());
|
||||||
|
}).flatMap(srcApplication -> {
|
||||||
|
Theme theme = new Theme();
|
||||||
|
theme.setName("theme_" + uniqueString);
|
||||||
|
return themeService.updateTheme(srcApplication.getId(), theme)
|
||||||
|
.then(themeService.persistCurrentTheme(srcApplication.getId(), theme))
|
||||||
|
.then(applicationService.findById(srcApplication.getId()));
|
||||||
|
}).flatMap(srcApplication -> {
|
||||||
|
Organization desOrg = new Organization();
|
||||||
|
desOrg.setName("org_dest_" + uniqueString);
|
||||||
|
return organizationService.create(desOrg).flatMap(createdOrg ->
|
||||||
|
applicationForkingService.forkApplicationToOrganization(srcApplication.getId(), createdOrg.getId())
|
||||||
|
).zipWith(Mono.just(srcApplication));
|
||||||
|
}).flatMap(applicationTuple2 -> {
|
||||||
|
Application forkedApp = applicationTuple2.getT1();
|
||||||
|
Application srcApp = applicationTuple2.getT2();
|
||||||
|
return Mono.zip(
|
||||||
|
themeService.getApplicationTheme(forkedApp.getId(), ApplicationMode.EDIT),
|
||||||
|
themeService.getApplicationTheme(forkedApp.getId(), ApplicationMode.PUBLISHED),
|
||||||
|
Mono.just(forkedApp),
|
||||||
|
Mono.just(srcApp)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
StepVerifier.create(tuple4Mono).assertNext(objects -> {
|
||||||
|
Theme editModeTheme = objects.getT1();
|
||||||
|
Theme publishedModeTheme = objects.getT2();
|
||||||
|
Application forkedApp = objects.getT3();
|
||||||
|
Application srcApp = objects.getT4();
|
||||||
|
|
||||||
|
assertThat(forkedApp.getEditModeThemeId()).isEqualTo(editModeTheme.getId());
|
||||||
|
assertThat(forkedApp.getPublishedModeThemeId()).isEqualTo(publishedModeTheme.getId());
|
||||||
|
assertThat(forkedApp.getEditModeThemeId()).isNotEqualTo(forkedApp.getPublishedModeThemeId());
|
||||||
|
|
||||||
|
// published mode should have the custom theme as we publish after forking the app
|
||||||
|
assertThat(publishedModeTheme.isSystemTheme()).isFalse();
|
||||||
|
|
||||||
|
// published mode theme will have no application id and org id set as it's a copy
|
||||||
|
assertThat(publishedModeTheme.getOrganizationId()).isNullOrEmpty();
|
||||||
|
assertThat(publishedModeTheme.getApplicationId()).isNullOrEmpty();
|
||||||
|
|
||||||
|
// edit mode theme should be a custom one
|
||||||
|
assertThat(editModeTheme.isSystemTheme()).isFalse();
|
||||||
|
|
||||||
|
// edit mode theme will have application id and org id set as the customizations were saved
|
||||||
|
assertThat(editModeTheme.getOrganizationId()).isNullOrEmpty();
|
||||||
|
assertThat(editModeTheme.getApplicationId()).isNullOrEmpty();
|
||||||
|
|
||||||
|
// forked theme should have the same name as src theme
|
||||||
|
assertThat(editModeTheme.getName()).isEqualTo("theme_" + uniqueString);
|
||||||
|
assertThat(publishedModeTheme.getName()).isEqualTo("theme_" + uniqueString);
|
||||||
|
|
||||||
|
// forked application should have a new edit mode theme created, should not be same as src app theme
|
||||||
|
assertThat(srcApp.getEditModeThemeId()).isNotEqualTo(forkedApp.getEditModeThemeId());
|
||||||
|
assertThat(srcApp.getPublishedModeThemeId()).isNotEqualTo(forkedApp.getPublishedModeThemeId());
|
||||||
|
}).verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
private Flux<ActionDTO> getActionsInOrganization(Organization organization) {
|
private Flux<ActionDTO> getActionsInOrganization(Organization organization) {
|
||||||
return applicationService
|
return applicationService
|
||||||
.findByOrganizationId(organization.getId(), READ_APPLICATIONS)
|
.findByOrganizationId(organization.getId(), READ_APPLICATIONS)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import com.appsmith.server.migrations.JsonSchemaVersions;
|
||||||
import com.appsmith.server.repositories.ApplicationRepository;
|
import com.appsmith.server.repositories.ApplicationRepository;
|
||||||
import com.appsmith.server.repositories.NewPageRepository;
|
import com.appsmith.server.repositories.NewPageRepository;
|
||||||
import com.appsmith.server.repositories.PluginRepository;
|
import com.appsmith.server.repositories.PluginRepository;
|
||||||
import com.appsmith.server.repositories.ThemeRepository;
|
|
||||||
import com.appsmith.server.services.ActionCollectionService;
|
import com.appsmith.server.services.ActionCollectionService;
|
||||||
import com.appsmith.server.services.ApplicationPageService;
|
import com.appsmith.server.services.ApplicationPageService;
|
||||||
import com.appsmith.server.services.DatasourceService;
|
import com.appsmith.server.services.DatasourceService;
|
||||||
|
|
@ -42,6 +41,7 @@ import com.appsmith.server.services.NewActionService;
|
||||||
import com.appsmith.server.services.NewPageService;
|
import com.appsmith.server.services.NewPageService;
|
||||||
import com.appsmith.server.services.OrganizationService;
|
import com.appsmith.server.services.OrganizationService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
|
import com.appsmith.server.services.ThemeService;
|
||||||
import com.appsmith.server.services.UserService;
|
import com.appsmith.server.services.UserService;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
@ -90,6 +90,7 @@ import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||||
|
import static com.appsmith.server.acl.AclPermission.MANAGE_THEMES;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
import static com.appsmith.server.acl.AclPermission.READ_PAGES;
|
||||||
|
|
@ -148,7 +149,7 @@ public class ImportExportApplicationServiceTests {
|
||||||
PluginExecutorHelper pluginExecutorHelper;
|
PluginExecutorHelper pluginExecutorHelper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
ThemeRepository themeRepository;
|
ThemeService themeService;
|
||||||
|
|
||||||
private static final String INVALID_JSON_FILE = "invalid json file";
|
private static final String INVALID_JSON_FILE = "invalid json file";
|
||||||
private static Plugin installedPlugin;
|
private static Plugin installedPlugin;
|
||||||
|
|
@ -859,8 +860,8 @@ public class ImportExportApplicationServiceTests {
|
||||||
.create(resultMono
|
.create(resultMono
|
||||||
.flatMap(application -> Mono.zip(
|
.flatMap(application -> Mono.zip(
|
||||||
Mono.just(application),
|
Mono.just(application),
|
||||||
themeRepository.findById(application.getEditModeThemeId()),
|
themeService.getThemeById(application.getEditModeThemeId(), MANAGE_THEMES),
|
||||||
themeRepository.findById(application.getPublishedModeThemeId())
|
themeService.getThemeById(application.getPublishedModeThemeId(), MANAGE_THEMES)
|
||||||
)))
|
)))
|
||||||
.assertNext(tuple -> {
|
.assertNext(tuple -> {
|
||||||
final Application application = tuple.getT1();
|
final Application application = tuple.getT1();
|
||||||
|
|
@ -869,9 +870,13 @@ public class ImportExportApplicationServiceTests {
|
||||||
|
|
||||||
assertThat(editTheme.isSystemTheme()).isFalse();
|
assertThat(editTheme.isSystemTheme()).isFalse();
|
||||||
assertThat(editTheme.getName()).isEqualTo("Custom edit theme");
|
assertThat(editTheme.getName()).isEqualTo("Custom edit theme");
|
||||||
|
assertThat(editTheme.getOrganizationId()).isNull();
|
||||||
|
assertThat(editTheme.getApplicationId()).isNull();
|
||||||
|
|
||||||
assertThat(publishedTheme.isSystemTheme()).isFalse();
|
assertThat(publishedTheme.isSystemTheme()).isFalse();
|
||||||
assertThat(publishedTheme.getName()).isEqualTo("Custom published theme");
|
assertThat(publishedTheme.getName()).isEqualTo("Custom published theme");
|
||||||
|
assertThat(publishedTheme.getOrganizationId()).isNullOrEmpty();
|
||||||
|
assertThat(publishedTheme.getApplicationId()).isNullOrEmpty();
|
||||||
})
|
})
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -632,11 +632,15 @@
|
||||||
"editModeTheme": {
|
"editModeTheme": {
|
||||||
"name": "Custom edit theme",
|
"name": "Custom edit theme",
|
||||||
"new": true,
|
"new": true,
|
||||||
"isSystemTheme": false
|
"isSystemTheme": false,
|
||||||
|
"applicationId": "dummy-app-id",
|
||||||
|
"organizationId": "dummy-org-id"
|
||||||
},
|
},
|
||||||
"publishedTheme": {
|
"publishedTheme": {
|
||||||
"name": "Custom published theme",
|
"name": "Custom published theme",
|
||||||
"new": true,
|
"new": true,
|
||||||
"isSystemTheme": false
|
"isSystemTheme": false,
|
||||||
|
"applicationId": "dummy-app-id",
|
||||||
|
"organizationId": "dummy-org-id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user