Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

confirmActionFunc is not supporting a compromised async function fix … #6739

Merged
merged 1 commit into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/entries/chunks/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export {
export { IsMobile, IsTouch, _setIsTouch } from "../../utils/devices";
export {
confirmAction,
confirmActionAsync,
detectIEOrEdge,
doKey2ClickUp,
doKey2ClickDown,
Expand Down
10 changes: 1 addition & 9 deletions src/knockout/koquestion_file.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import * as ko from "knockout";
import { Serializer,
Question,
QuestionFactory,
QuestionFileModel,
getOriginalEvent,
confirmAction,
detectIEOrEdge,
loadFileFromBase64
} from "survey-core";
import { Serializer, QuestionFactory, QuestionFileModel, getOriginalEvent } from "survey-core";
import { QuestionImplementor } from "./koquestion";

class QuestionFileImplementor extends QuestionImplementor {
Expand Down
20 changes: 11 additions & 9 deletions src/question_file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { property, propertyArray, Serializer } from "./jsonobject";
import { QuestionFactory } from "./questionfactory";
import { EventBase } from "./base";
import { UploadingFileError, ExceedSizeError } from "./error";
import { surveyLocalization } from "./surveyStrings";
import { SurveyError } from "./survey-error";
import { CssClassBuilder } from "./utils/cssClassBuilder";
import { confirmAction, detectIEOrEdge, loadFileFromBase64 } from "./utils/utils";
import { confirmActionAsync, detectIEOrEdge, loadFileFromBase64 } from "./utils/utils";
import { ActionContainer } from "./actions/container";
import { Action } from "./actions/action";
import { Helpers } from "./helpers";
Expand Down Expand Up @@ -582,23 +581,26 @@ export class QuestionFileModel extends Question {
this.onChange(src);
}
doClean = (event: any) => {
var src = event.currentTarget || event.srcElement;
if (this.needConfirmRemoveFile) {
var isConfirmed = confirmAction(this.confirmRemoveAllMessage);
if (!isConfirmed) return;
confirmActionAsync(this.confirmRemoveAllMessage, () => { this.clearFilesCore(); });
return;
}
this.clearFilesCore();
}
private clearFilesCore(): void {
if(this.rootElement) {
this.rootElement.querySelectorAll("input")[0].value = "";
}
this.clear();
}
doRemoveFile(data: any) {
if (this.needConfirmRemoveFile) {
var isConfirmed = confirmAction(
this.getConfirmRemoveMessage(data.name)
);
if (!isConfirmed) return;
confirmActionAsync(this.getConfirmRemoveMessage(data.name), () => { this.removeFileCore(data); });
return;
}
this.removeFileCore(data);
}
private removeFileCore(data: any): void {
const previewIndex = this.previewValue.indexOf(data);
this.removeFileByContent(previewIndex === -1 ? data : this.value[previewIndex]);
}
Expand Down
10 changes: 8 additions & 2 deletions src/question_matrixdynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { SurveyError } from "./survey-error";
import { MinRowCountError } from "./error";
import { IAction } from "./actions/action";
import { settings } from "./settings";
import { confirmAction } from "./utils/utils";
import { confirmActionAsync } from "./utils/utils";
import { DragDropMatrixRows } from "./dragdrop/matrix-rows";
import { IShortcutText, ISurveyImpl, IProgressInfo } from "./base-interfaces";
import { CssClassBuilder } from "./utils/cssClassBuilder";
Expand Down Expand Up @@ -566,7 +566,13 @@ export class QuestionMatrixDynamicModel extends QuestionMatrixDropdownModelBase
if(confirmDelete === undefined) {
confirmDelete = this.isRequireConfirmOnRowDelete(index);
}
if (confirmDelete && !confirmAction(this.confirmDeleteText)) return;
if (confirmDelete) {
confirmActionAsync(this.confirmDeleteText, () => { this.removeRowAsync(index, row); });
return;
}
this.removeRowAsync(index, row);
}
private removeRowAsync(index: number, row: MatrixDropdownRowModelBase): void {
if (!!row && !!this.survey && !this.survey.matrixRowRemoving(this, index, row)) return;
this.onStartRowAddingRemoving();
this.removeRowCore(index);
Expand Down
12 changes: 6 additions & 6 deletions src/question_paneldynamic.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { HashTable, Helpers } from "./helpers";

import {
IElement,
IQuestion,
Expand All @@ -11,8 +10,7 @@ import {
IProgressInfo,
} from "./base-interfaces";
import { SurveyElement } from "./survey-element";
import { surveyLocalization } from "./surveyStrings";
import { ILocalizableOwner, LocalizableString } from "./localizablestring";
import { LocalizableString } from "./localizablestring";
import {
TextPreProcessorValue,
QuestionTextProcessor,
Expand All @@ -23,7 +21,7 @@ import { JsonObject, property, Serializer } from "./jsonobject";
import { QuestionFactory } from "./questionfactory";
import { KeyDuplicationError } from "./error";
import { settings } from "./settings";
import { confirmAction, mergeValues } from "./utils/utils";
import { confirmActionAsync } from "./utils/utils";
import { SurveyError } from "./survey-error";
import { CssClassBuilder } from "./utils/cssClassBuilder";
import { ActionContainer } from "./actions/container";
Expand Down Expand Up @@ -1236,9 +1234,11 @@ export class QuestionPanelDynamicModel extends Question
* @see canRemovePanel
*
*/
public removePanelUI(value: any) {
public removePanelUI(value: any): void {
if (!this.canRemovePanel) return;
if (!this.confirmDelete || confirmAction(this.confirmDeleteText)) {
if(this.confirmDelete) {
confirmActionAsync(this.confirmDeleteText, () => { this.removePanel(value); });
} else {
this.removePanel(value);
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,16 @@ export var settings = {
confirmActionFunc: function (message: string): boolean {
return confirm(message);
},
/**
* A property that allows you to display a custom confirm dialog in async mode instead of the standard browser dialog. Set this property to a function that renders your custom dialog window in async mode.
* @param message A message to be displayed in the confirm dialog window.
* @param callback A callback function that should be called with res paramter equals to true if action is confirmed and equals to false otherwise.
*/
confirmActionAsyncFunc: function (message: string, resFunc: (res: boolean) => void): boolean {
//when you finish with displaying your dialog, call the resFunc as resFunc(true) or resFunc(false).
//You should return true to tell that you use this function
return false;
},
/**
* A minimum width value for all survey elements.
*
Expand Down
19 changes: 15 additions & 4 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,24 @@ function confirmAction(message: string): boolean {
return settings.confirmActionFunc(message);
return confirm(message);
}
function detectIEBrowser() {
function confirmActionAsync(message: string, funcOnYes: () => void, funcOnNo?: () => void): void {
const callbackFunc = (res: boolean): void => {
if(res) funcOnYes();
else if(!!funcOnNo) funcOnNo();
};
if(!!settings && !!settings.confirmActionAsyncFunc) {
if(settings.confirmActionAsyncFunc(message, callbackFunc)) return;
}
callbackFunc(confirmAction(message));
}
function detectIEBrowser(): boolean {
if (typeof window === "undefined") return false;
const ua: string = window.navigator.userAgent;
const oldIe: number = ua.indexOf("MSIE ");
const elevenIe: number = ua.indexOf("Trident/");
return oldIe > -1 || elevenIe > -1;
}
function detectIEOrEdge() {
function detectIEOrEdge(): boolean {
if (typeof window === "undefined") return false;
if (typeof (<any>detectIEOrEdge).isIEOrEdge === "undefined") {
const ua: string = window.navigator.userAgent;
Expand All @@ -37,7 +47,7 @@ function detectIEOrEdge() {
}
return (<any>detectIEOrEdge).isIEOrEdge;
}
function loadFileFromBase64(b64Data: string, fileName: string) {
function loadFileFromBase64(b64Data: string, fileName: string): void {
try {
const byteString: string = atob(b64Data.split(",")[1]);

Expand All @@ -64,7 +74,7 @@ function loadFileFromBase64(b64Data: string, fileName: string) {
}
} catch (err) { }
}
function isMobile() {
function isMobile(): boolean {
return (
typeof window !== "undefined" && typeof window.orientation !== "undefined"
);
Expand Down Expand Up @@ -392,6 +402,7 @@ export {
classesToSelector,
compareVersions,
confirmAction,
confirmActionAsync,
detectIEOrEdge,
detectIEBrowser,
loadFileFromBase64,
Expand Down
31 changes: 30 additions & 1 deletion tests/question_matrixdynamictests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8709,4 +8709,33 @@ QUnit.test("Errors: matrixdropdown + mobile mode", function (assert) {
assert.equal(table.rows[0].cells[2].hasQuestion, true);
assert.equal(table.rows[0].cells[3].isErrorsCell, true);
assert.equal(table.rows[0].cells[4].hasQuestion, true);
});
});
QUnit.test("matrixdynamic.removeRow & confirmActionAsyncFunc, #6736", function (assert) {
const prevAsync = settings.confirmActionAsyncFunc;

const survey = new SurveyModel({
elements: [
{ type: "matrixdynamic", name: "matrix",
columns: [{ name: "col1" }]
}
]
});
const q = <QuestionMatrixDynamicModel>survey.getQuestionByName("matrix");
q.value = [{ col1: 1 }, { col1: 2 }, { col1: 3 }];
let f_resFunc = (res: boolean): void => {};
settings.confirmActionAsyncFunc = (message: string, resFunc: (res: boolean) => void): boolean => {
f_resFunc = resFunc;
return true;
};
q.removeRow(1, true);
assert.equal(q.visibleRows.length, 3, "We are waiting for async function");
f_resFunc(false);
assert.equal(q.visibleRows.length, 3, "confirm action return false");
q.removeRow(1, true);
assert.equal(q.visibleRows.length, 3, "We are waiting for async function, #2");
f_resFunc(true);
assert.equal(q.visibleRows.length, 2, "confirm action return true");
assert.deepEqual(q.value, [{ col1: 1 }, { col1: 3 }], "Row is deleted correctly");

settings.confirmActionAsyncFunc = prevAsync;
});
37 changes: 37 additions & 0 deletions tests/surveypaneldynamictests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5979,3 +5979,40 @@ QUnit.test("Make sure that panel is not collapsed on focusing the question", fun
q2.focus();
assert.equal(survey.currentPageNo, 1);
});
QUnit.test("paneldynamic.removePanelUI & confirmActionAsyncFunc, #6736", function(assert) {
const prevAsync = settings.confirmActionAsyncFunc;
const survey = new SurveyModel({
questions: [
{
type: "paneldynamic",
name: "panel1",
templateElements: [
{
type: "text",
name: "q1",
},
]
},
],
});
const panel = <QuestionPanelDynamicModel>survey.getQuestionByName("panel1");
panel.confirmDelete = true;
panel.value = [{ q1: 1 }, { q1: 2 }, { q1: 3 }];
assert.equal(panel.panelCount, 3, "There are 3 panels by default");
let f_resFunc = (res: boolean): void => {};
settings.confirmActionAsyncFunc = (message: string, resFunc: (res: boolean) => void): boolean => {
f_resFunc = resFunc;
return true;
};
panel.removePanelUI(1);
assert.equal(panel.panelCount, 3, "We are waiting for async function");
f_resFunc(false);
assert.equal(panel.panelCount, 3, "confirm action return false");
panel.removePanelUI(1);
assert.equal(panel.panelCount, 3, "We are waiting for async function, #2");
f_resFunc(true);
assert.equal(panel.panelCount, 2, "confirm action return true");
assert.deepEqual(panel.value, [{ q1: 1 }, { q1: 3 }], "Row is deleted correctly");

settings.confirmActionAsyncFunc = prevAsync;
});