Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## Release 4.3.0

* We updated Data Widgets module compatibility to v3.5.0
* We updated Atlas Core module compatibility to v4.1.3
* We updated Atlas Web Content module compatibility to v4.1.0
* We fixed an issue where switching between the Minimal, Default and All views did not work correctly for the Audit Trail.

_______

## Release 4.2.0

* We added a conflict resolution feature that allows workflow administrators to resolve incompatible workflows in groups. For more information, refer to the Workflow Commons module documentation.
Expand Down
Binary file added Releases/WorkflowCommons-4-3-0.mpk
Binary file not shown.
Binary file modified Source/ExpenseRequestStarterApp.mpr
Binary file not shown.
25 changes: 25 additions & 0 deletions Source/javascriptsource/datawidgets/actions/Clear_Selection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import "mx-global";
import { Big } from "big.js";

// BEGIN EXTRA CODE
// END EXTRA CODE

/**
* @param {string} targetName - The name of the widget for which selection should be cleared.
* @returns {Promise.<void>}
*/
export async function Clear_Selection(targetName) {
// BEGIN USER CODE
const plugin = window["com.mendix.widgets.web.plugin.externalEvents"];
if (plugin) {
plugin.emit(targetName, "selection.clear");
}
// END USER CODE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import "mx-global";
import { Big } from "big.js";
import AsyncStorage from '@react-native-async-storage/async-storage';

// BEGIN EXTRA CODE
// END EXTRA CODE

/**
* @param {string} key
* @param {string} entity
* @returns {Promise.<MxObject>}
*/
export async function JS_GetFeedbackStorageObject(key, entity) {
// BEGIN USER CODE
if (!key) {
return Promise.reject(new Error("Input parameter 'Key' is required"));
}
if (!entity) {
return Promise.reject(new Error("Input parameter 'Entity' is required"));
}
return getItem(key).then(result => {
if (result === null) {
return Promise.reject(new Error(`Storage item '${key}' does not exist`));
}
const value = JSON.parse(result);
return getOrCreateMxObject(entity, value).then(newObject => {
const newValue = serializeMxObject(newObject);
return setItem(key, JSON.stringify(newValue)).then(() => newObject);
});
});
function getItem(key) {
if (navigator && navigator.product === "ReactNative") {
return AsyncStorage.getItem(key);
}
if (window) {
const value = window.localStorage.getItem(key);
return Promise.resolve(value);
}
return Promise.reject(new Error("No storage API available"));
}
function setItem(key, value) {
if (navigator && navigator.product === "ReactNative") {
return AsyncStorage.setItem(key, value);
}
if (window) {
window.localStorage.setItem(key, value);
return Promise.resolve();
}
return Promise.reject(new Error("No storage API available"));
}
function getOrCreateMxObject(entity, value) {
return getMxObject(value.guid).then(existingObject => {
if (existingObject) {
return existingObject;
}
else {
return createMxObject(entity, value);
}
});
}
function getMxObject(guid) {
return new Promise((resolve, reject) => {
mx.data.get({
guid,
callback: mxObject => resolve(mxObject),
error: error => reject(error)
});
});
}
function createMxObject(entity, value) {
return new Promise((resolve, reject) => {
mx.data.create({
entity,
callback: mxObject => {
Object.keys(value)
.filter(attribute => attribute !== "guid")
.forEach(attributeName => {
const attributeValue = value[attributeName];
mxObject.set(attributeName, attributeValue);
});
resolve(mxObject);
},
error: () => reject(new Error(`Could not create '${entity}' object`))
});
});
}
function serializeMxObject(object) {
return object.getAttributes().reduce((accumulator, attributeName) => {
accumulator[attributeName] = object.get(attributeName);
return accumulator;
}, { guid: object.getGuid() });
}
// END USER CODE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import "mx-global";
import { Big } from "big.js";

// BEGIN EXTRA CODE
// END EXTRA CODE

/**
* @param {string} localStorageKey
* @param {string} objectItemKey
* @returns {Promise.<string>}
*/
export async function JS_GetSingleLocalStorageObjectItem(localStorageKey, objectItemKey) {
// BEGIN USER CODE
if (!localStorageKey) {
return Promise.reject(new Error("Input parameter 'localStorageKey' is required"));
}
if (!objectItemKey) {
return Promise.reject(new Error("Input parameter 'objectItemKey' is required"));
}
const localObject = window.localStorage.getItem(localStorageKey);
const parsedObject = JSON.parse(localObject);
const singleItem = parsedObject?.[objectItemKey] ?? "";

return Promise.resolve(singleItem);
// END USER CODE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import "mx-global";
import { Big } from "big.js";
import { getUserRoleNames } from "mx-api/session";
import { ui, session } from "mx-api";

// BEGIN EXTRA CODE
const handleUserRoles = async () => {
try {
let userRoles;

if (
typeof mx !== "undefined" &&
typeof mx.session === "object" &&
typeof mx.session.getUserRoleNames === "function"
) {
userRoles = mx.session.getUserRoleNames();
} else if (typeof getUserRoleNames !== "function" || getUserRoleNames === undefined) {
userRoles = getUserRoleNames();
} else {
console.error("Feedback module cannot access a valid user role retrieval function.");
return undefined;
}

if (!Array.isArray(userRoles) || userRoles.length === 0) {
console.error("User roles not available or empty.");
return undefined;
}

return userRoles[0];
} catch (error) {
console.error("Feedback module failed to get the user role name.", error);
return undefined;
}
};

const handlePagePath = async () => {
try {
if (
typeof mx !== "undefined" &&
typeof mx.ui.getContentForm === "function" &&
typeof mx.ui.getContentForm().path !== "undefined"
) {
return mx.ui.getContentForm().path;
} else {
return window.history.state.pageName;
}
} catch(error) {
console.error("Feedback module cannot get the Mendix App page name", error);
return undefined;
}
};
// END EXTRA CODE

/**
* What does this JavaScript action do?
*
* Returns meta data from the clients internet browser.
*
* This includes;
*
* ActiveUserRoles
* PageName
* EnvironmentURL
* Browser
* ScreenWidth
* ScreenHeight
* @param {MxObject} feedback
* @returns {Promise.<MxObject>}
*/
export async function JS_PopulateFeedbackMetadata(feedback) {
// BEGIN USER CODE
try {
const userRoles = await handleUserRoles();
const pagePath = await handlePagePath();

feedback.set("ActiveUserRoles", userRoles || "");
feedback.set("PageName", pagePath || "");
feedback.set("EnvironmentURL", window.location.href || "");
feedback.set("Browser", navigator.userAgent || "");
feedback.set("ScreenWidth", window.screen.width || "");
feedback.set("ScreenHeight", window.screen.height || "");
return feedback;
} catch (error) {
console.error("Feedback Module cannot correctly set meta data.", error);
};
// END USER CODE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import "mx-global";
import { Big } from "big.js";

// BEGIN EXTRA CODE
// END EXTRA CODE

/**
* @returns {Promise.<void>}
*/
export async function JS_Recalculate_MendixModal_Error_PopUp_Zindex() {
// BEGIN USER CODE

function setModalZindex(cssSelector, zIndexValue) {
try {
const htmlElement = document.querySelectorAll(cssSelector);

if(!htmlElement.length) {
return;
}

htmlElement.forEach(item => item.style.zIndex = zIndexValue);

} catch(error) {
console.warn("Feedback Module JS Action JS_Recalculate_Modal_Zindex could not execute correctly.", error);
}
};

setTimeout(() => {
setModalZindex(".mx-dialog-info, mx-dialog-warning, .mx-dialog-error", "90");
setModalZindex(".mx-underlay", "80");
},500);
// END USER CODE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
import "mx-global";
import { Big } from "big.js";

// BEGIN EXTRA CODE
// END EXTRA CODE

/**
* What does this JavaScript action do?
*
* After you have uploaded an image it removes locally stored image from memory. This is a custom build action.
*
* Dependency Note:
* This JavaScript action should be used only when you have inserted the Image Upload JavaScript Action called 'JS_UploadAndConvertToFileBlobURL' into your nanoflow.
*
* More detailed explanation: Memory management.
*
* To upload a image we use a custom build Javascript action called 'JS_UploadAndConvertToFileBlobURL'.
* Inside this action we use a JavaScript method called createObjectURL() to upload and store files in local memory. We can access and cosume this in memory image resource via the URL path that is returned from the createObjectURL() method.
*
* However, each time you call createObjectURL(), a new object is created in memory, even if you've already created one for the same object.
* So each of these must be released by calling this action called 'JS_RevokeUploadedFileFromMemory' when you no longer need them.
*
* Browsers will release object URLs automatically when the document is unloaded; however, for optimal performance and memory usage, if there are safe times when you can explicitly unload them, you should do so with the JavaScriptAction called 'JS_RevokeUploadedFileFromMemory'.
* @param {string} fileBlobURL - You have to pass the fileBlobURL that was created using the URL.createObjectURL() in the JS Action called 'JS_UploadAndConvertToFileBlobURL'
* @returns {Promise.<void>}
*/
export async function JS_RevokeUploadedFileFromMemory(fileBlobURL) {
// BEGIN USER CODE
/* We use the URL.createObjectURL() static method which creates a string containing a URL representing the
image uploaded.
The image blob is stored in the clients browser and takes up memory whilst the session is active. So here we
revoke the image when the user deletes the image. Note that the image is automaticlly revoked when the browser refreshes
or closes.

You have to pass the fileBlobURL that was created using the URL.createObjectURL() in the JS Action called 'JS_UploadAndConvertToFileBlobURL'
*/
if(fileBlobURL && typeof fileBlobURL === "string"){
URL.revokeObjectURL(fileBlobURL);
} else {
throw new Error("Image was not removed from browser memory");
}
// END USER CODE
}
Loading