Skip to content
Snippets Groups Projects
Commit 55c1774e authored by Simon Leipold's avatar Simon Leipold
Browse files

[Projekt: Entwicklung - Neon][TicketNr.: 1056936][Alle Rechte für eine Rolle setzten]

parent 4cec61a9
No related branches found
No related tags found
No related merge requests found
Showing
with 248 additions and 171 deletions
......@@ -54,6 +54,10 @@
<name>PermissionCondition_param</name>
<expose v="true" />
</entityParameter>
<entityParameter>
<name>FullPermissions_param</name>
<expose v="true" />
</entityParameter>
</entityFields>
<recordContainers>
<jDitoRecordContainer>
......
......@@ -3,7 +3,8 @@ import("system.translate");
import("system.result");
var actionTitle = vars.get("$field.ACTION");
var fullPermission = vars.get("$param.FullPermissions_param");
if (actionTitle == "null" || actionTitle == undefined || actionTitle == null || actionTitle == "") {
if ((actionTitle == "null" || actionTitle == undefined || actionTitle == null || actionTitle == "") && fullPermission != 1) {
result.string(translate.text("Empty actions are invalid!"));
}
\ No newline at end of file
import("Sql_lib");
import("system.db");
import("system.tools");
import("system.result");
import("system.vars");
import("Permission_lib");
import("system.vars");
var alias = SqlUtils.getSystemAlias();
var permissionId = vars.exists("$param.PermissionId_param") && vars.get("$param.PermissionId_param");
var fullPermissions = vars.exists("$param.FullPermissions_param") && vars.get("$param.FullPermissions_param");
if (permissionId) {
var newAction = vars.get("$local.rowdata")["ACTION.value"];
PermissionUtil.insertAction(permissionId, newAction, vars.get("$field.UID"));
// create given actions if fullPermissions-flag is not set
// if flag is set, all sets, permissions and actions get created in PermissionDetail_entity.jDito.onInsert
if (fullPermissions == 0) {
var newAction = vars.get("$local.rowdata")["ACTION.value"];
PermissionUtil.insertAction(permissionId, newAction, vars.get("$field.UID"));
}
}
tools.clearPermissionCache();
\ No newline at end of file
......@@ -176,6 +176,7 @@
<entityConsumer>
<name>PermissionActions</name>
<refreshParent v="true" />
<stateProcess>%aditoprj%/entity/PermissionDetail_entity/entityfields/permissionactions/stateProcess.js</stateProcess>
<onValidation>%aditoprj%/entity/PermissionDetail_entity/entityfields/permissionactions/onValidation.js</onValidation>
<dependency>
<name>dependency</name>
......@@ -195,8 +196,17 @@
<name>PermissionCondition_param</name>
<valueProcess>%aditoprj%/entity/PermissionDetail_entity/entityfields/permissionactions/children/permissioncondition_param/valueProcess.js</valueProcess>
</entityParameter>
<entityParameter>
<name>FullPermissions_param</name>
<valueProcess>%aditoprj%/entity/PermissionDetail_entity/entityfields/permissionactions/children/fullpermissions_param/valueProcess.js</valueProcess>
</entityParameter>
</children>
</entityConsumer>
<entityField>
<name>FULLPERMISSIONS</name>
<title>Full Permissions</title>
<contentType>BOOLEAN</contentType>
</entityField>
</entityFields>
<recordContainers>
<jDitoRecordContainer>
......
import("system.vars");
import("system.result");
result.string(vars.get("$field.FULLPERMISSIONS"));
\ No newline at end of file
......@@ -15,6 +15,7 @@ var fieldTitle = vars.get("$field.FIELD");
var accesstype = vars.get("$field.ACCESSTYPE");
var permCondInput = vars.get("$field.CONDITION");
var permCondType = vars.get("$field.CONDTYPE");
var fullPermissions = vars.get("$field.FULLPERMISSIONS");
var permId = PermissionUtil.getPermission(roleName, entityName, fieldTitle, accesstype, permCondInput, permCondType);
var actionsInDb = PermissionUtil.getActions([permId]);
var actionsAsStringArray = vars.get("$field.ACTION").split(","); // only useful while working with already existing permissions
......@@ -25,92 +26,105 @@ var deletedRows = vars.get("$field.PermissionActions.deletedRows");
var changedRows = vars.get("$field.PermissionActions.changedRows");
var insertedRows = vars.get("$field.PermissionActions.insertedRows");
for each (let row in insertedRows) {
if (row.ACTION == "") {
result.string(translate.text("Empty actions are not allowed!"));
break;
if (fullPermissions == 0 || fullPermissions == "") {
for each (let row in insertedRows) {
if (row.ACTION == "") {
result.string(translate.text("Empty actions are not allowed!"));
break;
}
}
}
if (
(deletedRows.length == actionsAsStringArray.length && changedRows.length == 0 && insertedRows.length == 0)
|| (deletedRows.length == 0 && changedRows.length == 0 && insertedRows.length == 0)
) {
result.string(translate.text("Permissions without actions are invalid!"));
}
if (
(deletedRows.length == actionsAsStringArray.length && changedRows.length == 0 && insertedRows.length == 0)
|| (deletedRows.length == 0 && changedRows.length == 0 && insertedRows.length == 0)
) {
result.string(translate.text("Permissions without actions are invalid!"));
}
if (PermissionUtil.permissionExists(permId)) {
// old permission
// insert only possible, if the same action is not already linked to the permission
for each (let row in insertedRows) {
var actionHasJustBeenDeleted = false;
if (PermissionUtil.actionExists(row.ACTION, permId)) {
for each (let delRow in deletedRows) {
if (row.ACTION == delRow.ACTION) {
actionHasJustBeenDeleted = true;
if (PermissionUtil.permissionExists(permId)) {
// old permission
// insert only possible, if the same action is not already linked to the permission
for each (let row in insertedRows) {
var actionHasJustBeenDeleted = false;
if (PermissionUtil.actionExists(row.ACTION, permId)) {
for each (let delRow in deletedRows) {
if (row.ACTION == delRow.ACTION) {
actionHasJustBeenDeleted = true;
}
}
if (!actionHasJustBeenDeleted) {
result.string(translate.text("Action '" + row.ACTION + "' already linked to this permission."));
}
}
if (!actionHasJustBeenDeleted) {
result.string(translate.text("Action '" + row.ACTION + "' already linked to this permission."));
}
}
// if conditional permission: check if action is already linked to default permission
if (!isDefaultPermission) {
if (PermissionUtil.actionExists(row.ACTION, PermissionUtil.getPermissionWithoutCond(PermissionUtil.getSet(roleName, entityName, accesstype, fieldTitle)))) {
result.string(translate.text("Action '" + row.ACTION + "' is already linked to the default permission."));
// if conditional permission: check if action is already linked to default permission
if (!isDefaultPermission) {
if (PermissionUtil.actionExists(row.ACTION, PermissionUtil.getPermissionWithoutCond(PermissionUtil.getSet(roleName, entityName, accesstype, fieldTitle)))) {
result.string(translate.text("Action '" + row.ACTION + "' is already linked to the default permission."));
}
}
}
}
for each (let row in changedRows) {
if (PermissionUtil.actionExists(row.ACTION, permId) && actionsAsStringArray.indexOf(row.ACTION) == -1) {
result.string(translate.text("Action '" + row.ACTION + "' already linked to this permission."));
for each (let row in changedRows) {
if (PermissionUtil.actionExists(row.ACTION, permId) && actionsAsStringArray.indexOf(row.ACTION) == -1) {
result.string(translate.text("Action '" + row.ACTION + "' already linked to this permission."));
}
}
}
} else {
// new permission
if (insertedRows.length > 0) {
var defaultPerm = PermissionUtil.getPermission(roleName, entityName, fieldTitle, accesstype, emptyCond);
if (PermissionUtil.permissionExists(defaultPerm)) {
var defaultActions = PermissionUtil.getActions([defaultPerm]);
for each (let entry in insertedRows) {
for each (let action in defaultActions) {
if (entry.ACTION == PermissionUtil.resolveActionId(action)) {
result.string(translate.text("Action '" + entry.ACTION + "' is already linked to a permission with this role-entity-field-combination"));
} else {
// new permission
if (insertedRows.length > 0) {
var defaultPerm = PermissionUtil.getPermission(roleName, entityName, fieldTitle, accesstype, emptyCond);
if (PermissionUtil.permissionExists(defaultPerm)) {
var defaultActions = PermissionUtil.getActions([defaultPerm]);
for each (let entry in insertedRows) {
for each (let action in defaultActions) {
if (entry.ACTION == PermissionUtil.resolveActionId(action)) {
result.string(translate.text("Action '" + entry.ACTION + "' is already linked to a permission with this role-entity-field-combination"));
}
}
}
}
}
for (let i = 0; i < insertedRows.length-1; i++) {
for (let j = i + 1; j < insertedRows.length; j++) {
if (insertedRows[i].ACTION == insertedRows[j].ACTION) {
result.string(translate.text("No duplicates allowed: action '" + insertedRows[i].ACTION + "'"));
for (let i = 0; i < insertedRows.length-1; i++) {
for (let j = i + 1; j < insertedRows.length; j++) {
if (insertedRows[i].ACTION == insertedRows[j].ACTION) {
result.string(translate.text("No duplicates allowed: action '" + insertedRows[i].ACTION + "'"));
}
}
}
}
}
}
if (insertedRows.length > 0) {
switch (accesstype) {
case "E":
if (actionsInDb.length >= allowedNumberOfActionsForEntityPermissions && !actionHasJustBeenDeleted) {
result.string(translate.text("Only " + allowedNumberOfActionsForEntityPermissions + " actions allowed for this type of permission."));
}
break;
case "R":
if (actionsInDb.length >= allowedNumberOfActionsForRecordPermission && !actionHasJustBeenDeleted) {
result.string(translate.text("Only " + allowedNumberOfActionsForRecordPermission + " actions allowed for this type of permission."));
}
break;
case "F":
if (actionsInDb.length >= allowedNumberOfActionsForFieldPermissions && !actionHasJustBeenDeleted) {
result.string(translate.text("Only " + allowedNumberOfActionsForFieldPermissions + " actions allowed for this type of permission."));
}
break;
default:
result.string(translate.text("This error should never appear - contact administrator (PermissionDetail_entity.PermissionAction.onValidation)."));
if (insertedRows.length > 0) {
switch (accesstype) {
case "E":
if (actionsInDb.length >= allowedNumberOfActionsForEntityPermissions && !actionHasJustBeenDeleted) {
result.string(translate.text("Only " + allowedNumberOfActionsForEntityPermissions + " actions allowed for this type of permission."));
}
break;
case "R":
if (actionsInDb.length >= allowedNumberOfActionsForRecordPermission && !actionHasJustBeenDeleted) {
result.string(translate.text("Only " + allowedNumberOfActionsForRecordPermission + " actions allowed for this type of permission."));
}
break;
case "F":
if (actionsInDb.length >= allowedNumberOfActionsForFieldPermissions && !actionHasJustBeenDeleted) {
result.string(translate.text("Only " + allowedNumberOfActionsForFieldPermissions + " actions allowed for this type of permission."));
}
break;
default:
result.string(translate.text("This error should never appear - contact administrator (PermissionDetail_entity.PermissionAction.onValidation)."));
}
}
}
\ No newline at end of file
} else {
var entityPermSetId = PermissionUtil.getSetRoot(roleName, entityName);
var entityPermIds = PermissionUtil.getPermissions([entityPermSetId]);
var entityActions = PermissionUtil.getActions(entityPermIds);
var recordPermSetId = PermissionUtil.getSet(roleName, entityName, "R");
var recordPermIds = PermissionUtil.getPermissions([recordPermSetId]);
var recordActions = PermissionUtil.getActions(recordPermIds);
if (entityActions.length + recordActions.length == 5) {
result.string(translate.text("Full permissions already assigned"));
}
}
import("system.result");
import("system.vars");
import("system.neon");
if (vars.get("$field.FULLPERMISSIONS") == 1) {
result.string(neon.COMPONENTSTATE_INVISIBLE);
} else {
result.string(neon.COMPONENTSTATE_EDITABLE);
}
\ No newline at end of file
......@@ -8,43 +8,74 @@ import("system.tools");
import("Permission_lib");
import("Entity_lib");
const ACCESSLEVEL_ENTITY = "E";
const ACCESSLEVEL_RECORD = "R";
const ACCESSLEVEL_FIELD = "F";
const ACTION_VIEW = "view";
const ACTION_CREATE = "create";
const ACTION_READ = "read";
const ACTION_UPDATE = "update";
const ACTION_DELETE = "delete";
var alias = SqlUtils.getSystemAlias();
var tablename = "ASYS_PERMISSION";
var tableName = "ASYS_PERMISSION";
var sqlExt = "";
var permissionid = vars.get("$field.UID");
var role = vars.get("$field.ROLE");
var entity = vars.get("$field.ENTITY");
var field = vars.get("$field.FIELD");
var roleName = vars.get("$field.ROLE");
var entityName = vars.get("$field.ENTITY");
var fieldName = vars.get("$field.FIELD");
var accesstype = vars.get("$field.ACCESSTYPE").trim();
var condtype = vars.get("$field.CONDTYPE").trim();
var condition = vars.get("$field.CONDITION");
var fullPermissions = vars.get("$field.FULLPERMISSIONS");
if (fullPermissions == 1) {
// maybe remove all existing permissions for this role/entity-combo first, then create new full permission
// === ENTITY === //
var entitySetId = PermissionUtil.insertSet("", entityName, roleName, "", ACCESSLEVEL_ENTITY); // entity level set
var entityPermId = PermissionUtil.insertPermission(entitySetId, "", "1", null); // entity level permission
PermissionUtil.insertAction(entityPermId, ACTION_VIEW, null); // action view
PermissionUtil.insertAction(entityPermId, ACTION_CREATE, null); // action create
// === RECORD === //
var recordSetId = PermissionUtil.insertSet(entitySetId, entityName, roleName, "", ACCESSLEVEL_RECORD); // record level set
var recordPermId = PermissionUtil.insertPermission(recordSetId, "", "1", null); // record level permission
PermissionUtil.insertAction(recordPermId, ACTION_READ, null); // read
PermissionUtil.insertAction(recordPermId, ACTION_UPDATE, null); // update
PermissionUtil.insertAction(recordPermId, ACTION_DELETE, null); // delete
// === FIELD === //
// TODO: insert field set (only if fields with usePermission existing)
// var fieldSetId = PermissionUtil.insertSet(entitySetId, entityName, roleName, fieldName, ACCESSLEVEL_FIELD);
// TODO: insert field permission (for all fields with usePermission set)
if (checkInput([role, entity, accesstype, condtype])) {
} else if (checkInput([roleName, entityName, accesstype, condtype])) {
// permission set
var permissionsetid = PermissionUtil.getSet(role, entity, accesstype, field);
var permissionsetid = PermissionUtil.getSet(roleName, entityName, accesstype, fieldName);
if (permissionsetid == "") {
// no fitting permissionset found - insert new permissionset
var rootpermissionset;
if (accesstype == "E") {
if (accesstype == ACCESSLEVEL_ENTITY) {
rootpermissionset = "";
} else {
rootpermissionset = PermissionUtil.getSetRoot(role, entity);
rootpermissionset = PermissionUtil.getSetRoot(roleName, entityName);
}
permissionsetid = PermissionUtil.insertSet(rootpermissionset, entity, role, field, accesstype);
permissionsetid = PermissionUtil.insertSet(rootpermissionset, entityName, roleName, fieldName, accesstype);
}
// permission
var existingPerm = PermissionUtil.getPermission(role, entity, field, accesstype, condition);
var existingPerm = PermissionUtil.getPermission(roleName, entityName, fieldName, accesstype, condition);
if (existingPerm == "") {
// no existing perm found -> insert
PermissionUtil.insertPermission(permissionsetid, condition, condtype, permissionid);
} else {
// existing perm found -> update
PermissionUtil.updateIfDiff(existingPerm, permissionsetid, "ASYS_PERMISSIONSET_ID", tablename);
PermissionUtil.updateIfDiff(existingPerm, condition, "COND", tablename);
PermissionUtil.updateIfDiff(existingPerm, "1", "CONDTYPE", tablename);
PermissionUtil.updateIfDiff(existingPerm, permissionsetid, "ASYS_PERMISSIONSET_ID", tableName);
PermissionUtil.updateIfDiff(existingPerm, condition, "COND", tableName);
PermissionUtil.updateIfDiff(existingPerm, "1", "CONDTYPE", tableName);
}
// permission action gets created in PermissionAction_entity
......
......@@ -38,6 +38,10 @@
<name>71e01140-6581-4b5a-8c2c-e8eebe489f38</name>
<entityField>ACTION</entityField>
</entityFieldLink>
<entityFieldLink>
<name>dee103c6-7f6c-46ba-8844-ccb4c19e67be</name>
<entityField>FULLPERMISSIONS</entityField>
</entityFieldLink>
</fields>
</genericViewTemplate>
<neonViewReference>
......
......@@ -7,7 +7,7 @@ import("Sql_lib");
/**
* Provides functions to work with permissions.
* This includes permission sets, permissions, permisstion actions, roles and role hierarchies.
* This includes sets, permissions, actions, roles and role hierarchies.
* Don't instanciate this!
*
* @class
......@@ -32,12 +32,12 @@ function PermissionUtil () {}
}
/**
* Returns the ids of all subordinated permission sets of a given parent permission set.
*
* @param {String} pSetId id of the parent permission set
*
* @result {String[]} array with the ids of every subordinated permission set. The result can never be null.
*/
* Returns the ids of all subordinated permission sets of a given parent permission set.
*
* @param {String} pSetId id of the parent permission set
*
* @result {String[]} array with the ids of every subordinated permission set. The result can never be null.
*/
PermissionUtil.getChildSetsOfSet = function (pSetId)
{
return newSelect("ASYS_PERMISSIONSETID", alias)
......@@ -47,12 +47,12 @@ function PermissionUtil () {}
}
/**
* Returns all subordinated permission actions of a given permission set.
*
* @param {String} pSetId id of the parent permission set
*
* @result {String[]} array with the ids of every subordinated permission action. The result can never be null.
*/
* Returns all subordinated permission actions of a given permission set.
*
* @param {String} pSetId id of the parent permission set
*
* @result {String[]} array with the ids of every subordinated permission action. The result can never be null.
*/
PermissionUtil.getActionsOfSet = function (pSetId)
{
return newSelect("ASYS_PERMISSIONACTION.ASYS_PERMISSIONACTIONID", alias)
......@@ -63,12 +63,12 @@ function PermissionUtil () {}
}
/**
* Returns all permission actions of the given permissions.
*
* @param {String[]} pPermIds the ids of the permissions
*
* @result {String[]} returns ids of all permission actions. The result can never be null.
*/
* Returns all permission actions of the given permissions.
*
* @param {String[]} pPermIds the ids of the permissions
*
* @result {String[]} returns ids of all permission actions. The result can never be null.
*/
PermissionUtil.getActions = function(pPermIds) {
if (pPermIds.length <= 0)
return [];
......@@ -80,12 +80,12 @@ function PermissionUtil () {}
}
/**
* Returns the action of a given permission action id.
*
* @param {String} pActionId id of the action
*
* @result {String} title of action as readable string of the given action id. Never 'null', empty string if there is no result.
*/
* Returns the action of a given permission action id.
*
* @param {String} pActionId id of the action
*
* @result {String} title of action as readable string of the given action id. Never 'null', empty string if there is no result.
*/
PermissionUtil.resolveActionId = function (pActionId)
{
return newSelect("ASYS_PERMISSIONACTION.ACTION", alias)
......@@ -95,12 +95,12 @@ function PermissionUtil () {}
}
/**
* Converts a given array to an object with properties permissionid, entity, role, field, cond, action, accesstype, condtype.
*
* @param {String[]} pArr the array which should be converted to an object. Order of array: permid, entity, role, field, cond, action, accesstype, condtype.
*
* @result {{}} converted object
*/
* Converts a given array to an object with properties permissionid, entity, role, field, cond, action, accesstype, condtype.
*
* @param {String[]} pArr the array which should be converted to an object. Order of array: permid, entity, role, field, cond, action, accesstype, condtype.
*
* @result {{}} converted object
*/
PermissionUtil.convertArrToObj = function(pArr) {
var ret = pArr.map(function(x) {
return {
......@@ -118,14 +118,14 @@ function PermissionUtil () {}
}
/**
* Returns the first index at which a given permissionid can be found in an array, or -1 if it is not present.
*
* @param {String[]} pPermTable permission table
*
* @param {String} pPermId id of the permission
*
* @result {int} returns position (index) of the searched permission in the table, otherwise returns -1 if not found
*/
* Returns the first index at which a given permissionid can be found in an array, or -1 if it is not present.
*
* @param {String[]} pPermTable permission table
*
* @param {String} pPermId id of the permission
*
* @result {int} returns position (index) of the searched permission in the table, otherwise returns -1 if not found
*/
PermissionUtil.indexOfPermId = function(pPermTable, pPermId) {
var notFound = -1;
for (var i = 0; i < pPermTable.length; i++) {
......@@ -136,14 +136,14 @@ function PermissionUtil () {}
}
/**
* Checks a permission if the given actions are different to the actions in the database.
*
* @param {String} pPermId permission id to which the actions are linked to
*
* @param {String[]} pActionNew array of strings of new actions
*
* @result {String[]} returns the different elements
*/
* Checks a permission if the given actions are different to the actions in the database.
*
* @param {String} pPermId permission id to which the actions are linked to
*
* @param {String[]} pActionNew array of strings of new actions
*
* @result {String[]} returns the different elements
*/
PermissionUtil.getActionDiff = function(pPermId, pActionNew) {
var actionOld = newSelect("ACTION", alias)
.from("ASYS_PERMISSIONACTION")
......@@ -154,18 +154,18 @@ function PermissionUtil () {}
}
/**
* Checks if the given string is different to the string of a column in the given database table.
*
* @param {String} pId id of DB entry
*
* @param {String} pString string which has to be checked if different
*
* @param {String} pDbCol column to which the string is compared
*
* @param {String} pDbTable database table
*
* @result {Boolean} returns true if different, otherwise false
*/
* Checks if the given string is different to the string of a column in the given database table.
*
* @param {String} pId id of DB entry
*
* @param {String} pString string which has to be checked if different
*
* @param {String} pDbCol column to which the string is compared
*
* @param {String} pDbTable database table
*
* @result {Boolean} returns true if different, otherwise false
*/
PermissionUtil.isDiff = function(pId, pString, pDbCol, pDbTable) {
var stringDb = newSelect(pDbCol, alias)
.from(pDbTable)
......@@ -175,18 +175,18 @@ function PermissionUtil () {}
}
/**
* Updates the value of the column in table if the values are different.
*
* @param {String} pId id of DB entry
*
* @param {String} pValue string which gets checked if different
*
* @param {String} pDbCol column to which the string is compared
*
* @param {String} pDbTable database table
*
* @result {Integer} number of records that were updated
*/
* Updates the value of the column in table if the values are different.
*
* @param {String} pId id of DB entry
*
* @param {String} pValue string which gets checked if different
*
* @param {String} pDbCol column to which the string is compared
*
* @param {String} pDbTable database table
*
* @result {Integer} number of records that were updated
*/
PermissionUtil.updateIfDiff = function(pId, pValue, pDbCol, pDbTable) {
if (PermissionUtil.isDiff(pId, pValue, pDbCol, pDbTable)) {
var cols = [pDbCol];
......@@ -219,12 +219,12 @@ function PermissionUtil () {}
}
/**
* Gets the default permission of the root permission set.
*
* @param {String} pPermId id of the permission
*
* @result {String} returns id of the default permission of the root permission set. Never 'null', empty string if there is no result.
*/
* Gets the default permission of the root permission set.
*
* @param {String} pPermId id of the permission
*
* @result {String} returns id of the default permission of the root permission set. Never 'null', empty string if there is no result.
*/
PermissionUtil.getPermissionRoot = function(pPermId) {
var parentSet = [PermissionUtil.getParentSet(pPermId)];
......@@ -448,9 +448,9 @@ function PermissionUtil () {}
*
* @param {String} pCond condition of the permission, empty if no condition
*
* @param {String} pCondType condition Type of the permission, should nearly always be "true"
* @param {String} pCondType condition Type of the permission, should always be 1
*
* @param {String} pPermId id of the new permission (can be empty/null)
* @param {String} pPermId id of the new permission (pass null to automatically generate)
*
* @result {Integer} returns id of the inserted permission
*/
......@@ -480,7 +480,7 @@ function PermissionUtil () {}
*
* @param {String} pAction title of action (view, create,...), mandatory
*
* @param {String} pActionId id of the new permission action
* @param {String} pActionId id of the new permission action (pass null to automatically generate)
*
* @result {Integer} returns id of the inserted permission action, returns null if insert was not possible
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment