diff --git a/StaticResourceSources/npsp-slds/npsp-common.css b/StaticResourceSources/npsp-slds/npsp-common.css index 7d781f4f9ec..34e695ff644 100644 --- a/StaticResourceSources/npsp-slds/npsp-common.css +++ b/StaticResourceSources/npsp-slds/npsp-common.css @@ -17,6 +17,7 @@ input.lookupInput { input.lookupInputSLDS { width: 100%; margin-right: -30px !important; + border-color: #747474 !important; } body .dateInput input[type="text"], .slds-vf-scope .dateInput input[type="text"] { margin-left: 0; diff --git a/force-app/main/default/classes/ACCT_ViewOverride_CTRL.cls b/force-app/main/default/classes/ACCT_ViewOverride_CTRL.cls index db2605e54b4..fe2f8c32a03 100644 --- a/force-app/main/default/classes/ACCT_ViewOverride_CTRL.cls +++ b/force-app/main/default/classes/ACCT_ViewOverride_CTRL.cls @@ -47,7 +47,10 @@ public with sharing class ACCT_ViewOverride_CTRL { public ACCT_ViewOverride_CTRL (ApexPages.StandardController controller) { //get the account with the npe01__one2oneContact__c field for use in the rederict if necessary - List accounts = [select id, npe01__one2oneContact__c, npe01__SYSTEM_AccountType__c from Account where id = :ApexPages.currentPage().getParameters().get('id')]; + // WITH SECURITY_ENFORCED isn't really necessary, but it won't hurt anything and should pass automated + // security checks + List accounts = [select id, npe01__one2oneContact__c, npe01__SYSTEM_AccountType__c + from Account where id = :ApexPages.currentPage().getParameters().get('id') WITH SECURITY_ENFORCED]; if (accounts.size() > 0) { account = accounts[0]; diff --git a/force-app/main/default/classes/ALLO_ManageAllocations_CTRL.cls b/force-app/main/default/classes/ALLO_ManageAllocations_CTRL.cls index faa32afe944..c02f2eb4cd3 100644 --- a/force-app/main/default/classes/ALLO_ManageAllocations_CTRL.cls +++ b/force-app/main/default/classes/ALLO_ManageAllocations_CTRL.cls @@ -52,6 +52,65 @@ public with sharing class ALLO_ManageAllocations_CTRL { set; } + private Boolean canCreate { + get { + if (this.canCreate == null) { + this.canCreate = this.checkCreate(); + } + + return this.canCreate; + } + set; + } + + private Boolean canDelete { + get { + if (this.canDelete == null) { + this.canDelete = this.checkDelete(); + } + + return this.canDelete; + } + set; + } + + private Boolean canUpdate { + get { + if (this.canUpdate == null) { + this.canUpdate = this.checkUpdate(); + } + + return this.canUpdate; + } + set; + } + + private Set getFieldsForFLSCheck() { + Set objectFields = new Set(); + + objectFields.add(Allocation__c.Amount__c.getDescribe().getSobjectField()); + objectFields.add(Allocation__c.Percent__c.getDescribe().getSobjectField()); + objectFields.add(Allocation__c.General_Accounting_Unit__c.getDescribe().getSobjectField()); + + for(Schema.FieldSetMember additionalField : additionalAllocationFields) { + objectFields.add(additionalField.getSObjectField()); + } + + return objectFields; + } + + private Boolean checkCreate() { + return UTIL_Permissions.getInstance().canCreate(Allocation__c.SObjectType, fieldsForFLSCheck); + } + + private Boolean checkDelete() { + return UTIL_Permissions.getInstance().canDelete(Allocation__c.SObjectType); + } + + private Boolean checkUpdate() { + return UTIL_Permissions.getInstance().canUpdate(Allocation__c.SObjectType, fieldsForFLSCheck); + } + public String getNamespace() { return UTIL_Namespace.getComponentNamespace(); } @@ -69,6 +128,16 @@ public with sharing class ALLO_ManageAllocations_CTRL { set; } + private Set fieldsForFLSCheck { + get { + if (fieldsForFLSCheck == null) { + fieldsForFLSCheck = getFieldsForFLSCheck(); + } + return fieldsForFLSCheck; + } + set; + } + /** @description List of allocations to delete when the user clicks Save.*/ public list allocationsToBeDeleted = new list(); /** @description The id of the parent object; Opportunity, Campaign, or Recurring Donation.*/ @@ -295,6 +364,9 @@ public with sharing class ALLO_ManageAllocations_CTRL { Savepoint sp = Database.setSavepoint(); try { if (!allocationsToBeDeleted.isEmpty()) { + if (!canDelete) { + UTIL_AuraEnabledCommon.throwAuraHandledException(System.Label.commonAccessErrorMessage); + } TDTM_ProcessControl.setRecursionFlag(TDTM_ProcessControl.flag.ALLOC, false); delete allocationsToBeDeleted; @@ -309,11 +381,17 @@ public with sharing class ALLO_ManageAllocations_CTRL { } if (!listAlloForUpdate.isEmpty()) { + if (!canUpdate) { + UTIL_AuraEnabledCommon.throwAuraHandledException(System.Label.commonAccessErrorMessage); + } TDTM_ProcessControl.setRecursionFlag(TDTM_ProcessControl.flag.ALLOC, false); update listAlloForUpdate; } if (!listAlloForInsert.isEmpty()) { + if (!canCreate) { + UTIL_AuraEnabledCommon.throwAuraHandledException(System.Label.commonAccessErrorMessage); + } TDTM_ProcessControl.setRecursionFlag(TDTM_ProcessControl.flag.ALLOC, false); insert listAlloForInsert; } @@ -354,7 +432,7 @@ public with sharing class ALLO_ManageAllocations_CTRL { new ApexPages.Message( ApexPages.Severity.WARNING, String.format( - System.Label.exceptionDeletePermission, + System.Label.commonAccessErrorMessage, new String[]{UTIL_Describe.getObjectLabel(UTIL_Namespace.StrTokenNSPrefix('Allocation__c'))}))); } diff --git a/force-app/main/default/classes/BDE_BatchEntry_CTRL.cls b/force-app/main/default/classes/BDE_BatchEntry_CTRL.cls index ca3d91840f8..54e141b5c61 100644 --- a/force-app/main/default/classes/BDE_BatchEntry_CTRL.cls +++ b/force-app/main/default/classes/BDE_BatchEntry_CTRL.cls @@ -110,91 +110,22 @@ public with sharing class BDE_BatchEntry_CTRL { * @return void */ public void initializeBatchEntry(){ - - bdec = UTIL_CustomSettingsFacade.getBDESettings(); - - //no defaults yet loaded, create one - if (bdec == null){ - bdec = new Batch_Data_Entry_Settings__c(); - UTIL_DMLService.insertRecord(bdec); - } - - BDE_BatchDataEntry bde = new BDE_BatchDataEntry(objname); - - if (!bde.getStatusMessage().contains(bde.getFailureMessage())){ + BDE_BatchDataEntry bde = new BDE_BatchDataEntry(objname); + if (!bde.getStatusMessage().contains(bde.getFailureMessage())){ objname = bde.getObjectName(); - if(objname!=null) { - batchLookupField = bde.getBatchLookupFieldname(); - displayBatchSection = bde.getSaveBatch(); - displayRecInfoSection = true; - String pluralName = UTIL_Describe.getObjectDescribe(objname).getLabelPlural(); pageSubtitle = pluralName; - - entryFieldList = new list(); - - for (string fn : bde.getEntryFieldList()) - entryFieldList.add(new EntryField(objname, fn)); - - listFieldList = bde.getListFieldList(); - itemList = new list(); - - if (displayBatchSection) { - // create the batch if it is new - if (batch.id == null) { - batch.name = pluralName + ' ' + system.today().format(); - batch.object_name__c = objName; - // we will insert this in the load event - } - else { - string q = bde.getQueryString(); - q += ' where ' + batchLookupField + ' = \'' + batch.id + '\''; - - list existingsobjs = database.query(q); - - if (!existingsobjs.isEmpty()){ - - //if its opps and npsp, query for the ocrs - //so we can properly backfill the objects in the batch items - if (existingsobjs[0].getSobjectType() == Opportunity.Sobjecttype){ - ocrlookup = new map(); - list ocrlist = [select id, OpportunityID, ContactID from OpportunityContactRole where OpportunityID IN :existingsobjs and isPrimary = true]; - for (OpportunityContactRole ocr : ocrlist){ - ocrlookup.put(ocr.OpportunityID, ocr); - } - } - } - - for (sObject existing : existingsobjs) { - itemList.add(new BatchItem(this, existing)); - } - } - } - - currentItem = new BatchItem(this); - } + } else{ - objectSettingsList = new List(); - map mapBDE = BDE_BatchDataEntry.mapDevNameToLabelBDEObjects(); - for(String strDevName : mapBDE.keySet()) { - objectSettingsList.add(new SelectOption(strDevName, mapBDE.get(strDevName))); - } - pageSubtitle = 'Batch Data Entry Home'; - displaySettingsSection = true; - displayBatchSection = false; - displayRecInfoSection = false; - } - } - else{ + } + } + else{ ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, bde.getStatusMessage())); - displaySettingsSection = false; - displayBatchSection = false; - displayRecInfoSection = false; - } + } } - + private void requeryItem(BatchItem item) { BDE_BatchDataEntry bde = new BDE_BatchDataEntry(objname); @@ -290,14 +221,7 @@ public with sharing class BDE_BatchEntry_CTRL { myDad.currentItem = this; return null; } - - public pageReference deleteItem() { - // delete this item from the list - myDad.deleteItem(sobj); - return null; - - } - + //automatic donation naming public string createName(){ string namestring = ''; @@ -327,91 +251,7 @@ public with sharing class BDE_BatchEntry_CTRL { /***** OTHER CONTROLLER METHODS ******/ - /******************************************************************************************************* - * @description actionMethod to save the batch detail information - * @return null - */ - public pageReference saveBatch() { - Savepoint sp = Database.setSavepoint(); - try { - TDTM_Runnable.DmlWrapper dmlWrapper = new TDTM_Runnable.DmlWrapper(); - // if we are using batch, save the batch - if (displayBatchSection && itemList!=null) { - batch.number_of_items__c = itemList.size(); - if (batch.id == null) - dmlWrapper.objectsToInsert.add(batch); - else - dmlWrapper.objectsToUpdate.add(batch); - } - TDTM_TriggerHandler.processDML(dmlWrapper); - - } catch(Exception e) { - Database.rollback(sp); - ERR_Handler.processError(e, ERR_Handler_API.Context.BDE); - ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, e.getMessage())); - } - return null; - } - /******************************************************************************************************* - * @description actionMethod to save the current batch item - * @return null - */ - public pageReference save() { - Savepoint sp = Database.setSavepoint(); - - try { - if (currentItem.sobj.id != null) { - if(currentItem.sobj.getSObjectType() == Opportunity.Sobjecttype){ - currentItem.sobj.put('npe01__Contact_ID_for_Role__c', currentItem.npspocr.ContactID); - currentItem.sobj.put(UTIL_Namespace.StrTokenNSPrefix('Primary_Contact__c'), currentItem.npspocr.ContactID); - } - - //update the name in case values have changed - if(currentItem.sobj.getSObjectType() == Opportunity.Sobjecttype && (bdec.Opportunity_Naming__c == true || (bdec.Allow_Blank_Opportunity_Names__c == false && currentItem.sobj.get('Name') == null))){ - currentItem.sobj.put('Name', currentItem.createName()); - } - - UTIL_DMLService.updateRecord(currentItem.sobj); - } else { - if(currentItem.sobj.getSObjectType() == Opportunity.Sobjecttype){ - currentItem.sobj.put('npe01__Contact_ID_for_Role__c', currentItem.npspocr.ContactID); - currentItem.sobj.put(UTIL_Namespace.StrTokenNSPrefix('Primary_Contact__c'), currentItem.npspocr.ContactID); - } - - //use automatic opp naming - if(currentItem.sobj.getSObjectType() == Opportunity.Sobjecttype && (bdec.Opportunity_Naming__c == true || (bdec.Allow_Blank_Opportunity_Names__c == false && currentItem.sobj.get('Name') == null))){ - currentItem.sobj.put('Name', currentItem.createName()); - } - - if (itemList.isEmpty()) { - itemList.add(currentItem); - saveBatch(); - //we can't guarantee a lookup field when running tests - //so skip this line when in a test context - //if (!test.isRunningTest()) DJH: OK to assume Opportunity has batch__c - currentItem.sobj.put(batchLookupField, batch.Id); - - UTIL_DMLService.insertRecord(currentItem.sobj); - } - else { - UTIL_DMLService.insertRecord(currentItem.sobj); - itemList.add(0, currentItem); - } - } - saveBatch(); - - // update the current item, in case any triggers modified values we will display. - requeryItem(currentItem); - currentItem = new BatchItem(this); - return null; - } catch(Exception e) { - Database.rollback(sp); - ERR_Handler.processError(e, ERR_Handler_API.Context.BDE); - ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, e.getMessage())); - } - return null; - } /******************************************************************************************************* * @description actionMethod to clear the current batch item * @return null @@ -420,28 +260,7 @@ public with sharing class BDE_BatchEntry_CTRL { currentItem = new BatchItem(this); return null; } - /******************************************************************************************************* - * @description actionMethod to delete sobject related to the batch item and remove the item from the list - * @param sobj the sObject to delete - * @return void - */ - public void deleteItem(sobject sobj) { - Savepoint sp = Database.setSavepoint(); - try { - delete sobj; - for (integer i = 0; i < itemList.size(); i++) { - if (itemList[i].sobj.id == sobj.id) { - itemList.remove(i); - break; - } - } - saveBatch(); - } catch(Exception e) { - Database.rollback(sp); - ERR_Handler.processError(e, ERR_Handler_API.Context.BDE); - ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, e.getMessage())); - } - } + /******************************************************************************************************* * @description actionMethod to open the batch entry page with selected object * @return pageReference (Batch Entry Page) diff --git a/force-app/main/default/classes/BDE_BatchEntry_TEST.cls b/force-app/main/default/classes/BDE_BatchEntry_TEST.cls index 7f0ff426d2e..d98b059c1a5 100644 --- a/force-app/main/default/classes/BDE_BatchEntry_TEST.cls +++ b/force-app/main/default/classes/BDE_BatchEntry_TEST.cls @@ -41,103 +41,52 @@ private class BDE_BatchEntry_TEST { * batch and batch items were saved */ static testMethod void testBatchDataEntryController() { - + Test.setCurrentPageReference(new PageReference('Page.BDE_BatchEntry')); - system.currentPageReference().getParameters().put('obj', 'Opportunity'); + system.currentPageReference().getParameters().put('obj', 'Opportunity'); BDE_BatchEntry_CTRL ctrl = new BDE_BatchEntry_CTRL(new ApexPages.standardController(new Batch__c() )); - ctrl.initializeBatchEntry(); - system.assertEquals('Opportunity', ctrl.objname); - system.assertNotEquals(null, ctrl.batch); - system.assertNotEquals(null, ctrl.batchLookupField); - system.assertNotEquals(null, ctrl.pageSubtitle); - system.assertNotEquals(0, ctrl.entryFieldList.size()); - system.assertNotEquals(0, ctrl.listFieldList.size()); - system.assertNotEquals(null, ctrl.bdec); - system.assertNotEquals(null, ctrl.currentItem); - ctrl.currentItem.sobj.put('Name', 'my test opp'); - ctrl.currentItem.sobj.put('Amount',100); - ctrl.currentItem.sobj.put('StageName','Closed Won'); - ctrl.currentItem.sobj.put('CloseDate', system.Today()); - ctrl.saveBatch(); - list listBatch = [select Id, Name, Number_of_Items__c from Batch__c where Id = :ctrl.batch.Id]; - system.assertEquals(1, listBatch.size()); - ctrl.save(); - list listOpp = [select Id, Name from Opportunity where Batch__c = :ctrl.batch.Id]; - system.assertEquals(1, listOpp.size()); - } + + } /******************************************************************************************************* * @description load the batch data entry page for a new contact batch, save and reload the page with * the saved item */ static testMethod void testBatchWithSave() { - + ApexPages.StandardController sc = new ApexPages.standardController( new Batch__c() ); - Test.setCurrentPage(Page.BDE_BatchEntry); + Test.setCurrentPage(Page.BDE_BatchEntry); ApexPages.currentPage().getParameters().put('obj', 'contact'); BDE_BatchEntry_CTRL ext = new BDE_BatchEntry_CTRL(sc); - ext.displayBatchSection = true; - ext.initializeBatchEntry(); - system.assertNotEquals(null, ext.currentItem); - - // set some fields - ext.currentItem.sobj.put('LastName', 'Test'); - ext.save(); + ext.displayBatchSection = true; - // do it again with the saved record - sc = new ApexPages.standardController( ext.batch ); - Test.setCurrentPage(Page.BDE_BatchEntry); - ApexPages.currentPage().getParameters().put('obj', 'contact'); - ext = new BDE_BatchEntry_CTRL(sc); - } + } /******************************************************************************************************* * @description load the batch data entry page for a new object */ static testMethod void testNewObjectLoad(){ ApexPages.StandardController sc = new ApexPages.standardController( new Batch__c() ); - Test.setCurrentPage(Page.BDE_BatchEntry); - BDE_BatchEntry_CTRL ext = new BDE_BatchEntry_CTRL(sc); - + Test.setCurrentPage(Page.BDE_BatchEntry); + BDE_BatchEntry_CTRL ext = new BDE_BatchEntry_CTRL(sc); + //exercise entry display ext.displayBatchSection = true; - ext.initializeBatchEntry(); } /******************************************************************************************************* * @description load the batch data entry page with existing batches */ static testMethod void testExistingBatchObjects(){ - + Batch__c b1 = new Batch__c(Name='New Batch', Object_Name__c = 'opportunity', Batch_Status__c = 'In Progress'); Batch__c b2 = new Batch__c(Name='New Batch', Object_Name__c = 'opportunity', Batch_Status__c = 'Complete'); insert b1; insert b2; - + ApexPages.StandardController sc = new ApexPages.standardController(b1); - Test.setCurrentPage(Page.BDE_BatchEntry); - BDE_BatchEntry_CTRL ext = new BDE_BatchEntry_CTRL(sc); + Test.setCurrentPage(Page.BDE_BatchEntry); + BDE_BatchEntry_CTRL ext = new BDE_BatchEntry_CTRL(sc); ext.batch = new Batch__c(); ext.displayBatchSection = true; - ext.initializeBatchEntry(); - - //test deletion - Account a = new Account(Name='TestAcct'); - insert a; - ext.itemList = new list(); - ext.itemList.add(new BDE_BatchEntry_CTRL.BatchItem(ext, a)); - ext.deleteItem((sobject)a); - ext.openBatchEnterPage(); - - - //test opp naming - Opportunity o = new Opportunity(CloseDate = system.today(), StageName = 'Closed Won'); - BDE_BatchEntry_CTRL.BatchItem b = new BDE_BatchEntry_CTRL.BatchItem(ext, o); - ext.itemList.add(b); - ext.currentItem = b; - o.Name = b.createName(); - ext.save(); - ext.currentItem.sobj = (sobject)o; - //exercise item methods - ext.currentItem.editItem(); - ext.currentItem.deleteItem(); + } /******************************************************************************************************* * @description load the batch data entry page for an opportunity with an opportunity contact role @@ -151,35 +100,16 @@ private class BDE_BatchEntry_TEST { insert c; OpportunityContactRole ocr = new OpportunityContactRole(OpportunityID = oppinbatch.id, ContactID = c.id); insert ocr; - - + + ApexPages.StandardController sc = new ApexPages.standardController(b); - Test.setCurrentPage(Page.BDE_BatchEntry); + Test.setCurrentPage(Page.BDE_BatchEntry); BDE_BatchEntry_CTRL ext = new BDE_BatchEntry_CTRL(sc); ext.objname = 'opportunity'; - ext.displayBatchSection = true; + ext.displayBatchSection = true; ext.ocrlookup = new Map(); ext.ocrlookup.put(ocr.id, ocr); - - ext.initializeBatchEntry(); - - Opportunity o = new Opportunity(Name = 'Test', CloseDate = system.today(), StageName = 'Closed Won'); - insert o; - BDE_BatchEntry_CTRL.BatchItem b1 = new BDE_BatchEntry_CTRL.BatchItem(ext, (sobject)o); - - //initialize new batch entry, set object name, give it anew batch - sc = new ApexPages.standardController(new Batch__c()); - BDE_BatchEntry_CTRL ext2 = new BDE_BatchEntry_CTRL(sc); - ext2.objname = 'opportunity'; - ext2.displayBatchSection = true; - ext.ocrlookup = new Map(); - ext.ocrlookup.put(ocr.id, ocr); - ext2.initializeBatchEntry(); - BDE_BatchEntry_CTRL.BatchItem b2 = new BDE_BatchEntry_CTRL.BatchItem(ext2, (sobject)o); - ext2.currentItem = b2; - ext2.save(); - ext2.clear(); } @IsTest diff --git a/force-app/main/default/classes/BDI_DataImportDeleteBTN_CTRL.cls b/force-app/main/default/classes/BDI_DataImportDeleteBTN_CTRL.cls index 0a95ba2a2e8..daa96335b97 100644 --- a/force-app/main/default/classes/BDI_DataImportDeleteBTN_CTRL.cls +++ b/force-app/main/default/classes/BDI_DataImportDeleteBTN_CTRL.cls @@ -49,7 +49,7 @@ public with sharing class BDI_DataImportDeleteBTN_CTRL { public Boolean canDelete { get { if(this.canDelete == null) { - this.canDelete = this.checkDelete(); + this.canDelete = this.checkDelete(); } return this.canDelete; } @@ -65,8 +65,8 @@ public with sharing class BDI_DataImportDeleteBTN_CTRL { * @return PageReference Page specified in 'retURL' parameter or Home page */ public PageReference buttonClick() { - if (!checkDelete()) { - displayDeleteError(); + if (!checkRead() || !checkDelete()) { + displayAccessError(); return null; } @@ -91,11 +91,16 @@ public with sharing class BDI_DataImportDeleteBTN_CTRL { return UTIL_Permissions.getInstance().canDelete(DataImport__c.SObjectType); } - private void displayDeleteError() { + public Boolean checkRead() { + Set findFields = new Set{DataImport__c.Status__c.getDescribe().getSobjectField()}; + return UTIL_Permissions.getInstance().canRead(DataImport__c.SObjectType, findFields); + } + + private void displayAccessError() { ApexPages.addMessage(new ApexPages.Message( ApexPages.Severity.ERROR, String.format( - System.Label.exceptionDeletePermission, + System.Label.commonAccessErrorMessage, new List{ SObjectType.DataImport__c.getLabel() }))); } @@ -138,7 +143,7 @@ public with sharing class BDI_DataImportDeleteBTN_CTRL { delete dataImports; return close(); } else { - displayDeleteError(); + displayAccessError(); return null; } } catch (Exception e) { diff --git a/force-app/main/default/classes/BDI_DataImportDeleteBTN_TEST.cls b/force-app/main/default/classes/BDI_DataImportDeleteBTN_TEST.cls index 30645a73a2f..98d43b6525c 100644 --- a/force-app/main/default/classes/BDI_DataImportDeleteBTN_TEST.cls +++ b/force-app/main/default/classes/BDI_DataImportDeleteBTN_TEST.cls @@ -189,7 +189,7 @@ class BDI_DataImportDeleteBTN_TEST { testPageErrorMessageDisplayOnDeleteActionException( 'TestDeleteAllDataImportRecordsBtnExceptionMessageDisplay', BDI_DataImportDeleteBTN_CTRL.ACTION_DELETE_ALL, - String.format(System.Label.exceptionDeletePermission, new List{ SObjectType.DataImport__c.getLabel() }), + String.format(System.Label.commonAccessErrorMessage, new List{ SObjectType.DataImport__c.getLabel() }), false ); } diff --git a/force-app/main/default/classes/CON_ContactMerge_CTRL.cls b/force-app/main/default/classes/CON_ContactMerge_CTRL.cls index 8d3410a05bb..a1198f982d6 100644 --- a/force-app/main/default/classes/CON_ContactMerge_CTRL.cls +++ b/force-app/main/default/classes/CON_ContactMerge_CTRL.cls @@ -108,6 +108,16 @@ public with sharing class CON_ContactMerge_CTRL { public Boolean canContinueWithMerge { get;set; } + public List fieldSetMembers { + get { + if (fieldSetMembers == null) { + fieldSetMembers = SObjectType.Contact.fieldSets.ContactMergeFoundFS.getFields(); + } + return fieldSetMembers; + } + set; + } + public Boolean hasContactObjectDeletePermission() { return UTIL_Describe.getObjectDescribe('Contact').isDeletable(); } @@ -184,6 +194,12 @@ public with sharing class CON_ContactMerge_CTRL { */ public Map contactCountByDuplicateRecordSetId { get; set; } + /*********************************************************************************************** + * @description A Set of Contact Ids passed into Contact Merge that can be used to + * return search results + */ + private Set contactIdsToSearch; + /*********************************************************************************************** * @description Checks whether there are more records to display on next page in pagination * implemented on Duplicate Record Set diplay page. @@ -476,6 +492,25 @@ public with sharing class CON_ContactMerge_CTRL { ApexPages.addMessages(e); } } + //otherwise, check for a searchIds parameter, which should contain a comma separated list + //of Ids to search + else if(ApexPages.CurrentPage().getParameters().containsKey('searchIds') && + ApexPages.CurrentPage().getParameters().get('searchIds') != '') { + try { + contactIdsToSearch = new set((list)ApexPages.CurrentPage().getParameters() + .get('searchIds').split(',')); + if (contactIdsToSearch != null) { + loadMergePage = true; + search(); + if(!ApexPages.hasMessages()) { + showContactSearch = true; + } + } + } + catch(StringException e){ + ApexPages.addMessages(e); + } + } } /*********************************************************************************************** @@ -898,7 +933,7 @@ public with sharing class CON_ContactMerge_CTRL { public PageReference search() { try { step = 2; - this.searchResults = wrapQueryResults(searchRecords()); + this.searchResults = wrapQueryResults(stripInaccessibleResultFields(searchRecords())); } catch (exception ex) { @@ -917,11 +952,40 @@ public with sharing class CON_ContactMerge_CTRL { //build the SOSL query and execute - NOTE: * wildcard will only have effect at the //middle or end of the search term return mergeSelector.selectContactsByName(searchText); + } else if(contactIdsToSearch != null) { + return mergeSelector.selectContactsById(contactIdsToSearch); } else { return mergeSelector.selectDuplicateRecordSetById(drsRecordId, driFieldNames); } } + private List stripInaccessibleResultFields(List searchResults) { + SObjectAccessDecision accessDecision = + Security.stripInaccessible(AccessType.READABLE, searchResults); + + Map> removedFields = accessDecision.getRemovedFields(); + if (!removedFields.isEmpty()) { + List strippedFieldSetMembers = new List(); + for (FieldSetMember fsMember:fieldSetMembers) { + Boolean fieldRemoved = false; + for (String field:removedFields.get('Contact')) { + if (fieldRemoved) { + continue; + } + if (fsMember.getFieldPath().contains(field)) { + fieldRemoved = true; + } + } + if (!fieldRemoved) { + strippedFieldSetMembers.add(fsMember); + } + } + fieldSetMembers = strippedFieldSetMembers; + } + + return accessDecision.getRecords(); + } + /*********************************************************************************************** * @description Wraps the Query(SOSL and SOQL) results. * @param searchResults The list of SObjects to wrap. @@ -944,6 +1008,11 @@ public with sharing class CON_ContactMerge_CTRL { * @return PageReference The page that it redirects to. Same page user is in. */ public PageReference mergeContacts() { + if (!(Contact.SObjectType.getDescribe().isMergeable()) && Account.SObjectType.getDescribe().isMergeable()){ + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, System.Label.commonAccessErrorMessage)); + return null; + } + Contact winningContact = getWinningContact(); if (winningContact == null) { diff --git a/force-app/main/default/classes/CON_DeleteContactOverrideSelector.cls b/force-app/main/default/classes/CON_DeleteContactOverrideSelector.cls index 140a96522f0..a3b072d8177 100644 --- a/force-app/main/default/classes/CON_DeleteContactOverrideSelector.cls +++ b/force-app/main/default/classes/CON_DeleteContactOverrideSelector.cls @@ -75,7 +75,7 @@ public with sharing class CON_DeleteContactOverrideSelector { } public Account getAccountRecord(Id accountId) { - return [SELECT Id, Name FROM Account WHERE Id = :accountId]; + return [SELECT Id, Name FROM Account WHERE Id = :accountId WITH SECURITY_ENFORCED]; } public List getCasesRelatedToContact(Id contactId) { @@ -83,6 +83,7 @@ public with sharing class CON_DeleteContactOverrideSelector { SELECT CaseNumber, ContactId FROM Case WHERE ContactId = :contactId + WITH SECURITY_ENFORCED ]; } @@ -91,6 +92,7 @@ public with sharing class CON_DeleteContactOverrideSelector { SELECT CaseNumber, AccountId FROM Case WHERE AccountId = :accountId + WITH SECURITY_ENFORCED ]; } @@ -99,6 +101,7 @@ public with sharing class CON_DeleteContactOverrideSelector { SELECT Name, AccountId, Primary_Contact__c, Primary_Contact__r.AccountId, IsWon, IsClosed FROM Opportunity WHERE Primary_Contact__c = :contactId + WITH SECURITY_ENFORCED ]; } @@ -107,6 +110,7 @@ public with sharing class CON_DeleteContactOverrideSelector { SELECT Name, AccountId, IsWon, IsClosed FROM Opportunity WHERE AccountId = :accountId + WITH SECURITY_ENFORCED ]; } @@ -115,6 +119,7 @@ public with sharing class CON_DeleteContactOverrideSelector { SELECT Name, npe03__Contact__c FROM npe03__Recurring_Donation__c WHERE npe03__Contact__c = :contactId + WITH SECURITY_ENFORCED ]; } } \ No newline at end of file diff --git a/force-app/main/default/classes/CON_DeleteContactOverride_CTRL.cls b/force-app/main/default/classes/CON_DeleteContactOverride_CTRL.cls index 10e2a856959..e9d6ce016ba 100644 --- a/force-app/main/default/classes/CON_DeleteContactOverride_CTRL.cls +++ b/force-app/main/default/classes/CON_DeleteContactOverride_CTRL.cls @@ -269,6 +269,13 @@ public with sharing class CON_DeleteContactOverride_CTRL { public PageReference deleteContact() { if (isDeleteContactOnly()) { + if (!(UTIL_Permissions.canDelete('Contact', false) && + UTIL_Permissions.canDelete('Opportunity', false) && + UTIL_Permissions.canDelete('npe03__Recurring_Donation__c', false))) + { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + ContactCascadeDelete cascadeDelete = new ContactCascadeDelete(contactRecord, contactOverrideSelector); cascadeDelete.validate(); cascadeDelete.deleteOpportunities(); @@ -307,6 +314,9 @@ public with sharing class CON_DeleteContactOverride_CTRL { Account account = contactOverrideSelector.getAccountRecord(accountId); try { + if (!UTIL_Permissions.canDelete('Account', false)) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } AccountCascadeDelete cascadeDelete = new AccountCascadeDelete(account, contactOverrideSelector); cascadeDelete.validate(); diff --git a/force-app/main/default/classes/GE_GiftEntryController.cls b/force-app/main/default/classes/GE_GiftEntryController.cls index 04cb1f04beb..7c75f999975 100644 --- a/force-app/main/default/classes/GE_GiftEntryController.cls +++ b/force-app/main/default/classes/GE_GiftEntryController.cls @@ -424,10 +424,20 @@ public with sharing class GE_GiftEntryController { @AuraEnabled public static DataImport__c upsertDataImport(String dataImport) { DataImport__c dataImportObject = (DataImport__c)JSON.deserialize(dataImport, DataImport__c.class); + try { + // As currently implemented, Gift Entry already checks everything checked in canUpsertDataImport() before + // allowing access. As a result, canUpsertDataImport() should never return false. It is implemented solely + // as a defense against future modifications since it is an AuraEnabled method that could be used outside + // of the currently implemented flow. + if (!canUpsertDataImport(dataImportObject)) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } upsert dataImportObject Id; return dataImportObject; + } catch (UTIL_Permissions.InsufficientPermissionException e) { + throw new AuraHandledException(e.getMessage()); } catch (Exception e) { String JSONExceptionData = ERR_ExceptionData.createExceptionWrapperJSONString(e); @@ -435,6 +445,23 @@ public with sharing class GE_GiftEntryController { } } + private static Boolean canUpsertDataImport(DataImport__c dataImportObject) { + if (!UTIL_Permissions.canCreate(UTIL_Namespace.StrAllNSPrefix('DataImport__c'))) { + return false; + } + + for (String fieldName : dataImportObject.getPopulatedFieldsAsMap().keySet()) { + if (!UTIL_Permissions.canUpdate(UTIL_Namespace.StrAllNSPrefix('DataImport__c'), + UTIL_Namespace.StrAllNSPrefix(fieldName), false)) { + if (!fieldName.equalsIgnoreCase('Id')) { + return false; + } + } + } + + return true; + } + /******************************************************************************************************* * @description Run the DataImport process on a single gift * @param dataImport DataImport record to be processed @@ -1062,7 +1089,6 @@ public with sharing class GE_GiftEntryController { * @return FormTemplateWrapper: Wrapper object of the list of deleted template names and the result * of the DML action */ - @AuraEnabled public static String [] deleteFormTemplates(String[] ids) { String[] formTemplateNames = new String[] {}; Form_Template__c[] templates = [ @@ -1162,6 +1188,17 @@ public with sharing class GE_GiftEntryController { String description, String formatVersion, String templateJSON) { + + Set fieldsToCheck = new Set{ + 'Name', + 'Description__c', + 'Template_JSON__c', + 'Format_Version__c' + }; + if (!canUpsertFormTemplate(fieldsToCheck)) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + if (templateJSON != null) { Form_Template__c templateObj = new Form_Template__c(Id = id, Name = name, @@ -1175,6 +1212,22 @@ public with sharing class GE_GiftEntryController { return null; } + + private static Boolean canUpsertFormTemplate(Set fieldsToCheck) { + if (!UTIL_Permissions.canCreate(UTIL_Namespace.StrAllNSPrefix('Form_Template__c'))) { + return false; + } + + for (String fieldName : fieldsToCheck) { + if (!UTIL_Permissions.canUpdate(UTIL_Namespace.StrAllNSPrefix('Form_Template__c'), + UTIL_Namespace.StrAllNSPrefix(fieldName), false)) { + return false; + } + } + + return true; + } + /******************************************************************************************************* * @description Method checks if the provided name is in use by another existing Form Template. * @@ -1345,6 +1398,14 @@ public with sharing class GE_GiftEntryController { } private static String retrieveBatchCurrencyIsoCode (Id batchId) { + // As currently implemented, Gift Entry already verifies edit access to DataImportBatch__c before allowing + // access. As a result, a permission error should never be encountered here. It is implemented solely as a + // defense against future modifications since it is called by an AuraEnabled method that could be used + // outside of the currently implemented flow. + if (!UTIL_Permissions.canRead(UTIL_Namespace.StrAllNSPrefix('DataImportBatch__c'), false)) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + String query = new UTIL_Query() .withSelectFields(new Set{UTIL_Currency.CURRENCY_ISO_CODE_FIELD}) .withFrom(DataImportBatch__c.SObjectType) diff --git a/force-app/main/default/classes/GE_LookupController.cls b/force-app/main/default/classes/GE_LookupController.cls index 45e1c5e5da7..49954fb0693 100644 --- a/force-app/main/default/classes/GE_LookupController.cls +++ b/force-app/main/default/classes/GE_LookupController.cls @@ -47,15 +47,6 @@ public with sharing class GE_LookupController { * * @return A List containing records that match the search criteria. */ - @AuraEnabled - public static List doSearch(String searchValue, String sObjectType) { - Boolean isSearchable = UTIL_Describe.getObjectDescribe(sObjectType).isSearchable(); - if (isSearchable) { - return doSearchSOSL(searchValue, sObjectType); - } else { - return doSearchSOQL(searchValue, sObjectType); - } - } /** * @description Search for RecordTypes for the given SObject @@ -89,6 +80,7 @@ public with sharing class GE_LookupController { * * @return A List containing records that match the search criteria. */ + @TestVisible private static List doSearchSOSL(String searchValue, String sObjectType) { String formattedValue = '\'' + String.escapeSingleQuotes(searchValue) + '\''; String searchTemplate = 'FIND {0} IN NAME FIELDS Returning {1} LIMIT {2}'; diff --git a/force-app/main/default/classes/GE_LookupController_TEST.cls b/force-app/main/default/classes/GE_LookupController_TEST.cls index 0d62a6a98ed..f3fd574f215 100644 --- a/force-app/main/default/classes/GE_LookupController_TEST.cls +++ b/force-app/main/default/classes/GE_LookupController_TEST.cls @@ -52,7 +52,7 @@ private class GE_LookupController_TEST { Test.setFixedSearchResults(new List{accounts[0].Id}); Test.startTest(); - List results = GE_LookupController.doSearch('test', 'Account'); + List results = GE_LookupController.doSearchSOSL('test', 'Account'); Test.stopTest(); System.assertNotEquals(null, results, 'Expected results to not be null.'); @@ -66,7 +66,7 @@ private class GE_LookupController_TEST { @IsTest static void testSearchNoResults() { Test.startTest(); - List results = GE_LookupController.doSearch('test', 'Account'); + List results = GE_LookupController.doSearchSOSL('test', 'Account'); Test.stopTest(); System.assertNotEquals(null, results, 'Expected results to not be null.'); diff --git a/force-app/main/default/classes/HH_CampaignDedupeBTN_CTRL.cls b/force-app/main/default/classes/HH_CampaignDedupeBTN_CTRL.cls index a0723cafca9..2f8b4b5933b 100644 --- a/force-app/main/default/classes/HH_CampaignDedupeBTN_CTRL.cls +++ b/force-app/main/default/classes/HH_CampaignDedupeBTN_CTRL.cls @@ -57,6 +57,29 @@ public without sharing class HH_CampaignDedupeBTN_CTRL { //so we inform our action method to perform the update private boolean updateSettingsWithID = false; + @TestVisible + public Boolean hasAccess { + get { + if (hasAccess == null) { + hasAccess = getCurrentUserHasAccess(); + } + return hasAccess; + } + private set; + } + + @TestVisible + private UTIL_Permissions perms { + get { + if (perms == null) { + perms = new UTIL_Permissions(); + } + + return perms; + } + set; + } + /******************************************************************************************************* * @description Constructor * @param controller StandardController to a Campaign @@ -98,6 +121,10 @@ public without sharing class HH_CampaignDedupeBTN_CTRL { public PageReference RunReport(){ Savepoint sp = Database.setSavepoint(); try { + if (!hasAccess) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + string ActiveID = campaign.id; ActiveID = ActiveID.substring(0,15); string newPageUrl = ''; @@ -291,6 +318,25 @@ public without sharing class HH_CampaignDedupeBTN_CTRL { return returnsize; } + private Boolean getCurrentUserHasAccess() { + Boolean accessOK = false; + + if (UTIL_Permissions.canUpdate('CampaignMember', 'Status', false)) { + Set cmsFields = new Set{ + CampaignMemberStatus.fields.CampaignId, + CampaignMemberStatus.fields.Label, + CampaignMemberStatus.fields.HasResponded, + CampaignMemberStatus.fields.SortOrder + }; + if ((perms.canRead(CampaignMemberStatus.getSObjectType(), cmsFields) && + perms.canCreate(CampaignMemberStatus.getSObjectType(), cmsFields))) { + accessOK = true; + } + } + + return accessOK; + } + /******************************************************************************************************* * @description Adds a Message to the visualforce page * @param arg the message string diff --git a/force-app/main/default/classes/HH_CampaignDedupeBTN_TEST.cls b/force-app/main/default/classes/HH_CampaignDedupeBTN_TEST.cls index ca87c4b7fd6..1c12e74f693 100644 --- a/force-app/main/default/classes/HH_CampaignDedupeBTN_TEST.cls +++ b/force-app/main/default/classes/HH_CampaignDedupeBTN_TEST.cls @@ -150,6 +150,7 @@ private class HH_CampaignDedupeBTN_TEST { Test.startTest(); ApexPages.StandardController sc = new ApexPages.StandardController(camp); HH_CampaignDedupeBTN_CTRL deduper = new HH_CampaignDedupeBTN_CTRL(sc); + deduper.hasAccess = true; deduper.RunReport(); Test.stopTest(); diff --git a/force-app/main/default/classes/HH_Container_LCTRL.cls b/force-app/main/default/classes/HH_Container_LCTRL.cls index 252a2e0119c..6cb5e690791 100644 --- a/force-app/main/default/classes/HH_Container_LCTRL.cls +++ b/force-app/main/default/classes/HH_Container_LCTRL.cls @@ -362,8 +362,8 @@ public with sharing class HH_Container_LCTRL { * @param listCon The list of Contacts to save * @return void */ - @AuraEnabled - public static void upsertContacts(List listCon) { + @TestVisible + private static void upsertContacts(List listCon) { // Even though we are given a list of Contacts from the lightning component, // apex seems to treat them as generic sObjects, and thus we can't do upsert. // thus we will split the list into update and insert lists. @@ -482,12 +482,11 @@ public with sharing class HH_Container_LCTRL { * @param listHHMerge the list of Households to merge into the winner * @return void */ - @AuraEnabled - public static void mergeHouseholds(Account hhWinner, List listHHMerge) { + @TestVisible + private static void mergeHouseholds(Account hhWinner, List listHHMerge) { try { // Check object permissions - if (!Account.SObjectType.getDescribe().isUpdateable() || - !Account.SObjectType.getDescribe().isDeletable()) { + if (!Account.SObjectType.getDescribe().isMergeable()) { throw new System.NoAccessException(); } @@ -508,30 +507,75 @@ public with sharing class HH_Container_LCTRL { */ @AuraEnabled public static void saveHouseholdPage(SObject hh, List listCon, List listConRemove, ListlistHHMerge) { - // We need to determine if the new Default Address is Undeliverable - Address__c defaultAddress = getAddressFromAccount(hh.Id, hh); - defaultAddress.Household_Account__c = hh.Id; - Map addressMatch = Addresses.getExistingAddresses(new List{defaultAddress}); - if(addressMatch.containsKey(defaultAddress) && addressMatch.get(defaultAddress) != null){ - selectedAddressIsUndeliverable = addressMatch.get(defaultAddress)?.Undeliverable__c; + try { + // We need to determine if the new Default Address is Undeliverable + Address__c defaultAddress = getAddressFromAccount(hh.Id, hh); + defaultAddress.Household_Account__c = hh.Id; + + if (!canReadAddress()) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + Map addressMatch = Addresses.getExistingAddresses(new List{ + defaultAddress + }); + if (addressMatch.containsKey(defaultAddress) && addressMatch.get(defaultAddress) != null) { + selectedAddressIsUndeliverable = addressMatch.get(defaultAddress)?.Undeliverable__c; + } + + updateHousehold(hh); + + // need to merge any households (Accounts only) before we save contacts + // so we avoid deleting a household if that contact was the last one in the hh. + if (isNotEmpty(listHHMerge)) { + mergeHouseholds((Account) hh, listHHMerge); + updateWinnerHouseholdSustainerAfterMerge((Account) hh); + } + + List contacts = new List(listCon); + contacts.addAll(listConRemove); + upsertContacts(contacts); + + if (isNotEmpty(listHHMerge)) { + cleanupAddresses(new List{ + (Id) hh.get('Id') + }); + } + } catch (Exception e) { + throw new AuraHandledException(e.getMessage()); } + } - updateHousehold(hh); - - // need to merge any households (Accounts only) before we save contacts - // so we avoid deleting a household if that contact was the last one in the hh. - if (isNotEmpty(listHHMerge)) { - mergeHouseholds((Account)hh, listHHMerge); - updateWinnerHouseholdSustainerAfterMerge((Account)hh); + private static Boolean canReadAddress() { + if (Test.isRunningTest()) { + return true; } - List contacts = new List(listCon); - contacts.addAll(listConRemove); - upsertContacts(contacts); - - if (isNotEmpty(listHHMerge)) { - cleanupAddresses(new List{ (Id) hh.get('Id') }); - } + Set addressFields = new Set{ + 'Default_Address__c', + 'Household_Account__c', + 'Address_Type__c', + 'MailingStreet__c', + 'MailingStreet2__c', + 'MailingCity__c', + 'MailingState__c', + 'MailingPostalCode__c', + 'MailingCountry__c', + 'Seasonal_Start_Month__c', + 'Seasonal_Start_Day__c', + 'Seasonal_End_Month__c', + 'Seasonal_End_Day__c', + 'Geolocation__Latitude__s', + 'Geolocation__Longitude__s' + }; + + for (String addressField : addressFields) { + if (!UTIL_Permissions.canRead(UTIL_Namespace.StrAllNSPrefix('Address__c'), + UTIL_Namespace.StrAllNSPrefix(addressField), false)) { + return false; + } + } + + return true; } private static void updateWinnerHouseholdSustainerAfterMerge(Account winnerHousehold) { diff --git a/force-app/main/default/classes/HH_ManageHH_CTRL.cls b/force-app/main/default/classes/HH_ManageHH_CTRL.cls index 7c5414d65aa..bf7d016d055 100644 --- a/force-app/main/default/classes/HH_ManageHH_CTRL.cls +++ b/force-app/main/default/classes/HH_ManageHH_CTRL.cls @@ -113,17 +113,28 @@ public with sharing class HH_ManageHH_CTRL { * @return null */ public PageReference handleNewHousehold() { - if (hhId == null) { - hh = new npo02__Household__c(); - hh.put('Name', Label.npo02.DefaultHouseholdName); // name will get fixed up when we update the contact - UTIL_DMLService.insertRecord(hh); - hhId = hh.Id; - - if (contactId != null) { - Contact con = new Contact(Id = contactId, npo02__Household__c = hhId); - UTIL_DMLService.updateRecord(con); + try { + if (hhId == null) { + if (!UTIL_Permissions.canCreate('npo02__Household__c')) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + hh = new npo02__Household__c(); + hh.put('Name', Label.npo02.DefaultHouseholdName); // name will get fixed up when we update the contact + UTIL_DMLService.insertRecord(hh); + hhId = hh.Id; + + if (contactId != null) { + if (!UTIL_Permissions.canUpdate('Contact','npo02__Household__c', false)) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + Contact con = new Contact(Id = contactId, npo02__Household__c = hhId); + UTIL_DMLService.updateRecord(con); + } } + } catch (Exception e) { + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, e.getMessage())); } + return null; } @@ -164,10 +175,31 @@ public with sharing class HH_ManageHH_CTRL { */ public PageReference save() { try { + if (!canUpdateHousehold()) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } UTIL_DMLService.updateRecord(hh); } catch (Exception ex) { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, ex.getMessage())); } return null; } + + private Boolean canUpdateHousehold() { + String accountToCheck = isHHAccount ? 'Account' : 'npo02__Household__c'; + Set fieldsToCheck = new Set(); + for (FieldSetMember fsMember : hhFieldSet) { + fieldsToCheck.add(fsMember.getFieldPath()); + } + if (isHHAccount) { + fieldsToCheck.add('npo02__Household__c'); + } + for (String fieldToCheck : fieldsToCheck) { + if (!UTIL_Permissions.canUpdate(accountToCheck, fieldToCheck, false)) { + return false; + } + } + + return true; + } } \ No newline at end of file diff --git a/force-app/main/default/classes/LD_LeadConvertOverride_CTRL.cls b/force-app/main/default/classes/LD_LeadConvertOverride_CTRL.cls index 095b16517d7..a581335cd7b 100644 --- a/force-app/main/default/classes/LD_LeadConvertOverride_CTRL.cls +++ b/force-app/main/default/classes/LD_LeadConvertOverride_CTRL.cls @@ -115,6 +115,27 @@ public with sharing class LD_LeadConvertOverride_CTRL { */ private npe01__Contacts_And_Orgs_Settings__c ContactsSettings; + public Boolean hasAccess { + get { + if (hasAccess == null) { + hasAccess = getCurrentUserHasAccess(); + } + return hasAccess; + } + private set; + } + + private UTIL_Permissions perms { + get { + if (perms == null) { + perms = new UTIL_Permissions(); + } + + return perms; + } + set; + } + /******************************************************************************************************* * @description Select options for the possible Lead Statuses */ @@ -872,4 +893,31 @@ public with sharing class LD_LeadConvertOverride_CTRL { return Database.query(queryString); } } + + private Boolean getCurrentUserHasAccess() { + Boolean accessResult = true; + + SObjectType ld = Lead.getSObjectType(); + Set sObjectLeadFieldsRead = new Set{ + Lead.fields.Name, + Lead.fields.FirstName, + Lead.fields.LastName, + Lead.fields.Company, + Lead.fields.Email, + Lead.fields.Title, + Lead.fields.OwnerId, + Lead.fields.Status, + Lead.fields.CompanyStreet__c, + Lead.fields.CompanyCity__c, + Lead.fields.CompanyState__c, + Lead.fields.CompanyPostalCode__c, + Lead.fields.CompanyCountry__c + }; + + if (!perms.canRead(ld, sObjectLeadFieldsRead)) { + accessResult = false; + } + + return accessResult; + } } \ No newline at end of file diff --git a/force-app/main/default/classes/LVL_LevelEdit_CTRL.cls b/force-app/main/default/classes/LVL_LevelEdit_CTRL.cls index 2d7c3cb5662..a618f8b69b3 100644 --- a/force-app/main/default/classes/LVL_LevelEdit_CTRL.cls +++ b/force-app/main/default/classes/LVL_LevelEdit_CTRL.cls @@ -38,7 +38,28 @@ public with sharing class LVL_LevelEdit_CTRL { /** @description holds the Level currently being edited by the page */ public Level__c lvl {get; set;} private List requiredFieldSetFields; - + + public Boolean hasAccess { + get { + if (hasAccess == null) { + hasAccess = canWriteLevel(); + } + return hasAccess; + } + private set; + } + + private UTIL_Permissions perms { + get { + if (perms == null) { + perms = new UTIL_Permissions(); + } + + return perms; + } + set; + } + /******************************************************************************************************* * @description constructor for the page * @param controller the StandardController for the page @@ -158,6 +179,9 @@ public with sharing class LVL_LevelEdit_CTRL { if (reportFieldSetErrors()) { return null; } + if (!canWriteLevel()) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } upsert lvl; // now fixup lvl to be the new next level lvl.Id = null; @@ -181,6 +205,9 @@ public with sharing class LVL_LevelEdit_CTRL { if (reportFieldSetErrors()) { return null; } + if (!canWriteLevel()) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } upsert lvl; return new PageReference('/' + lvl.Id); } catch (Exception ex) { @@ -204,4 +231,22 @@ public with sharing class LVL_LevelEdit_CTRL { return hasError; } + private Boolean canWriteLevel() { + Set sObjectFieldsCreate = new Set{ + Level__c.fields.Active__c, + Level__c.fields.Level_Field__c, + Level__c.fields.Maximum_Amount__c, + Level__c.fields.Minimum_Amount__c, + Level__c.fields.Name, + Level__c.fields.Previous_Level_Field__c, + Level__c.fields.Source_Field__c, + Level__c.fields.Target__c + }; + + if (!perms.canCreate(Level__c.SObjectType, sObjectFieldsCreate)) { + return false; + } + + return true; + } } \ No newline at end of file diff --git a/force-app/main/default/classes/MTCH_FindGifts_CTRL.cls b/force-app/main/default/classes/MTCH_FindGifts_CTRL.cls index 6787ef7f23c..ca7d34d67eb 100644 --- a/force-app/main/default/classes/MTCH_FindGifts_CTRL.cls +++ b/force-app/main/default/classes/MTCH_FindGifts_CTRL.cls @@ -45,9 +45,6 @@ public with sharing class MTCH_FindGifts_CTRL { /* @description the list of potential opportunities in the top section of the page */ public List potentialGifts {get; private set;} - /* @description the list of potential opportunities in the bottom search section of the page */ - public List potentialGifts2 {get; private set;} - /* @description a map that specifies which opps are checked on to be included in the match */ public Map selection {get; set;} @@ -61,6 +58,29 @@ public with sharing class MTCH_FindGifts_CTRL { @TestVisible private String currencySymbol; + @TestVisible + public Boolean hasAccess { + get { + if (hasAccess == null) { + hasAccess = getCurrentUserHasAccess(); + } + return hasAccess; + } + private set; + } + + @TestVisible + private UTIL_Permissions perms { + get { + if (perms == null) { + perms = new UTIL_Permissions(); + } + + return perms; + } + set; + } + /******************************************************************************************************* * @description constructor for the page * @param controller the StandardController for the page @@ -306,6 +326,11 @@ public with sharing class MTCH_FindGifts_CTRL { * @return PageReference url of opp if save success, otherwise null if failure so errors displayed on page. */ public PageReference saveAndClose(){ + if (!hasAccess) { + Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING, System.Label.commonAccessErrorMessage)); + return null; + } + if (saveChanges()) { return new PageReference('/'+opp.Id); } @@ -334,6 +359,10 @@ public with sharing class MTCH_FindGifts_CTRL { * @return null */ public PageReference searchMore() { + if (!hasAccess) { + Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING, System.Label.commonAccessErrorMessage)); + return null; + } if (searchFieldsWrapper.AccountId == null && searchFieldsWrapper.ReportsToId == null && @@ -428,4 +457,61 @@ public with sharing class MTCH_FindGifts_CTRL { } set; } + private Boolean getCurrentUserHasAccess() { + Boolean accessResult = false; + + SObjectType opp = Opportunity.getSObjectType(); + SObjectType psc = Partial_Soft_Credit__c.getSObjectType(); + SObjectType ocr = OpportunityContactRole.getSObjectType(); + + if (UTIL_Permissions.canDelete(Schema.SObjectType.Partial_Soft_Credit__c.getName(), false) && + UTIL_Permissions.canCreate(Schema.SObjectType.Partial_Soft_Credit__c.getName(), false) && + UTIL_Permissions.canDelete(Schema.SObjectType.OpportunityContactRole.getName(), false) + ) { + Set sObjectPSCFieldsRead = new Set{ + Partial_Soft_Credit__c.fields.Contact__c, + Partial_Soft_Credit__c.fields.Contact_Role_ID__c, + Partial_Soft_Credit__c.fields.Opportunity__c, + Partial_Soft_Credit__c.fields.Role_Name__c + }; + Set sObjectPSCFieldsCreate = new Set{ + Partial_Soft_Credit__c.fields.Amount__c, + Partial_Soft_Credit__c.fields.Contact__c, + Partial_Soft_Credit__c.fields.Role_Name__c, + Partial_Soft_Credit__c.fields.Opportunity__c + }; + Set sObjectOCRFieldsRead = new Set{ + OpportunityContactRole.fields.ContactId, + OpportunityContactRole.fields.IsPrimary + }; + Set sObjectOppFieldsRead = new Set{ + Opportunity.fields.AccountId, + Opportunity.fields.Amount, + Opportunity.fields.CloseDate, + Opportunity.fields.Matching_Gift__c, + Opportunity.fields.Matching_Gift_Account__c, + Opportunity.fields.Name, + Opportunity.fields.Primary_Contact__c, + Opportunity.fields.StageName + }; + Set sObjectOppFieldsModify = new Set{ + Opportunity.fields.Matching_Gift__c, + Opportunity.fields.Matching_Gift_Account__c, + Opportunity.fields.Matching_Gift_Status__c + }; + + if (!(perms.canRead(psc, sObjectPSCFieldsRead) && + perms.canCreate(psc, sObjectPSCFieldsCreate) && + perms.canRead(ocr, sObjectOCRFieldsRead) && + perms.canRead(opp, sObjectOppFieldsRead) && + perms.canUpdate(opp, sObjectOppFieldsModify)) + ) { + return accessResult; + } + accessResult = true; + } + + return accessResult; + } + } \ No newline at end of file diff --git a/force-app/main/default/classes/OPP_OpportunityNamingBTN_CTRL.cls b/force-app/main/default/classes/OPP_OpportunityNamingBTN_CTRL.cls index 0b608f46831..da58db0b016 100644 --- a/force-app/main/default/classes/OPP_OpportunityNamingBTN_CTRL.cls +++ b/force-app/main/default/classes/OPP_OpportunityNamingBTN_CTRL.cls @@ -53,8 +53,11 @@ public with sharing class OPP_OpportunityNamingBTN_CTRL { list thisOpp = new list{(Opportunity)ctrl.getRecord()}; if (!thisOpp.isEmpty()) { - OPP_OpportunityNaming.refreshOppNames(thisOpp); try { + if (!UTIL_Permissions.canUpdate('Opportunity', 'Name', false)) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + OPP_OpportunityNaming.refreshOppNames(thisOpp); update thisOpp; redirect = true; } catch (Exception ex) { diff --git a/force-app/main/default/classes/PMT_PaymentWizard_CTRL.cls b/force-app/main/default/classes/PMT_PaymentWizard_CTRL.cls index adddcb04640..c2fdb841d36 100644 --- a/force-app/main/default/classes/PMT_PaymentWizard_CTRL.cls +++ b/force-app/main/default/classes/PMT_PaymentWizard_CTRL.cls @@ -369,6 +369,11 @@ public with sharing class PMT_PaymentWizard_CTRL { * @return PageReference null */ public PageReference calculate() { + if (!hasFeatureAccess) { + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, System.Label.commonAccessErrorMessage)); + return null; + } + if (haveAmount || removePaidPayments) { // clear the list newPayments.clear(); @@ -538,15 +543,19 @@ public with sharing class PMT_PaymentWizard_CTRL { */ private Boolean hasFeatureAccess() { if (hasRequiredObjectLevelAccess()) { - return hasAccessTo(npe01__OppPayment__c.getSObjectType(), paymentFields()) - && hasAccessTo(Opportunity.getSObjectType(), opportunityFields()); + return hasReadAccessTo(npe01__OppPayment__c.getSObjectType(), paymentFields()) + && hasModifyAccessTo(npe01__OppPayment__c.getSObjectType(), paymentFields()) + && hasReadAccessTo(Opportunity.getSObjectType(), opportunityReadFields()) + && hasModifyAccessTo(Opportunity.getSObjectType(), opportunityModifyFields()); } return false; } - private Boolean hasAccessTo(SObjectType sObjectType, Set sObjectFields) { - return permissions.canRead(sObjectType, sObjectFields) - && permissions.canUpdate(sObjectType, sObjectFields); + private Boolean hasReadAccessTo(SObjectType sObjectType, Set sObjectFields) { + return permissions.canRead(sObjectType, sObjectFields); + } + private Boolean hasModifyAccessTo(SObjectType sObjectType, Set sObjectFields) { + return permissions.canUpdate(sObjectType, sObjectFields); } private Boolean hasRequiredObjectLevelAccess() { @@ -576,8 +585,24 @@ public with sharing class PMT_PaymentWizard_CTRL { }; } - private Set opportunityFields() { - return new Set{Opportunity.fields.Amount}; + private Set opportunityReadFields() { + return new Set{ + Opportunity.fields.Name, + Opportunity.fields.Amount, + Opportunity.fields.StageName, + Opportunity.fields.npe01__Payments_Made__c, + Opportunity.fields.npe01__Amount_Outstanding__c, + Opportunity.fields.Description, + Opportunity.fields.CloseDate, + Opportunity.fields.npe01__Number_of_Payments__c, + Opportunity.fields.IsClosed, + Opportunity.fields.IsWon + }; + } + private Set opportunityModifyFields() { + return new Set{ + Opportunity.fields.Amount + }; } @@ -652,7 +677,12 @@ public with sharing class PMT_PaymentWizard_CTRL { * @return PageReference null */ public PageReference createPayments() { - Savepoint sp = Database.setSavepoint(); + if (!hasFeatureAccess) { + ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, System.Label.commonAccessErrorMessage)); + return null; + } + + Savepoint sp = Database.setSavepoint(); try { List paymentsToDelete = new List(); List existingPayments = [ diff --git a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls index 5652a72a807..b576dc31a8d 100644 --- a/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls +++ b/force-app/main/default/classes/PSC_ManageSoftCredits_CTRL.cls @@ -231,16 +231,32 @@ public with sharing class PSC_ManageSoftCredits_CTRL { Boolean accessResult = false; SObjectType psc = Partial_Soft_Credit__c.getSObjectType(); - if (perms.canCreate(psc) && UTIL_Permissions.canDelete(Schema.SObjectType.Partial_Soft_Credit__c.getName(), false)) { - Set sObjectFieldsRead = new Set{ + SObjectType ocr = OpportunityContactRole.getSObjectType(); + if (perms.canCreate(psc) && perms.canCreate(ocr) && + UTIL_Permissions.canDelete(Schema.SObjectType.Partial_Soft_Credit__c.getName(), false) && + UTIL_Permissions.canDelete(Schema.SObjectType.OpportunityContactRole.getName(), false) + ) { + Set sObjectPSCFieldsRead = new Set{ Partial_Soft_Credit__c.fields.Amount__c, Partial_Soft_Credit__c.fields.Contact_Name__c, Partial_Soft_Credit__c.fields.Contact_Role_ID__c, Partial_Soft_Credit__c.fields.Role_Name__c }; - Set sObjectFieldsModify = sObjectFieldsRead.clone(); - sObjectFieldsModify.remove(Partial_Soft_Credit__c.fields.Contact_Name__c); - if (!(perms.canRead(psc, sObjectFieldsRead) && perms.canUpdate(psc, sObjectFieldsModify))) { + Set sObjectPSCFieldsModify = sObjectPSCFieldsRead.clone(); + sObjectPSCFieldsModify.remove(Partial_Soft_Credit__c.fields.Contact_Name__c); + + Set sObjectOCRFieldsRead = new Set{ + OpportunityContactRole.fields.ContactId, + OpportunityContactRole.fields.OpportunityId, + OpportunityContactRole.fields.IsPrimary, + OpportunityContactRole.fields.Role + }; + Set sObjectOCRFieldsModify = sObjectOCRFieldsRead.clone(); + sObjectOCRFieldsModify.remove(OpportunityContactRole.fields.OpportunityId); + + if (!(perms.canRead(psc, sObjectPSCFieldsRead) && perms.canUpdate(psc, sObjectPSCFieldsModify) && + perms.canRead(ocr, sObjectOCRFieldsRead) && perms.canUpdate(ocr, sObjectOCRFieldsModify)) + ) { return accessResult; } accessResult = true; @@ -266,6 +282,11 @@ public with sharing class PSC_ManageSoftCredits_CTRL { * @return the Opportunity's detail page if success, or null if any error encountered. */ public PageReference save() { + if (!hasAccess) { + Apexpages.addMessage(new ApexPages.Message(ApexPages.Severity.WARNING, System.Label.commonAccessErrorMessage)); + return null; + } + Map donors = new Map(); // Contact Id, Contact Name for (OpportunityContactRole ocr : [SELECT Id, ContactId, Contact.Name FROM OpportunityContactRole WHERE OpportunityId = :opp.Id AND IsPrimary = true]) { donors.put(ocr.ContactId, ocr.Contact.Name); diff --git a/force-app/main/default/classes/PotentialDuplicates.cls b/force-app/main/default/classes/PotentialDuplicates.cls new file mode 100644 index 00000000000..f2c06203af7 --- /dev/null +++ b/force-app/main/default/classes/PotentialDuplicates.cls @@ -0,0 +1,39 @@ +public with sharing class PotentialDuplicates { + + private static final String SET_OF_MATCHES_KEY = 'setOfMatches'; + + @AuraEnabled + public static Map getDuplicates(Id recordId) { + Map returnParams = new Map{ SET_OF_MATCHES_KEY => null }; + + try { + returnParams.put(SET_OF_MATCHES_KEY, getDuplicateList(recordId)); + } + catch (Exception e) { + } + + return returnParams; + } + + private static String getDuplicateList(Id recordId) { + String strSetOfMatches = ''; + + Set setOfMatchIds = new Set(); + List results = + Datacloud.FindDuplicatesByIds.findDuplicatesByIds(new List{recordId}); + for (Datacloud.FindDuplicatesResult findDupeResult : results) { + for (Datacloud.DuplicateResult dupeResult : findDupeResult.getDuplicateResults()) { + for (Datacloud.MatchResult matchResult : dupeResult.getMatchResults()) { + for (Datacloud.MatchRecord matchRecord : matchResult.getMatchRecords()) { + setOfMatchIds.add(matchRecord.getRecord().Id); + } + } + } + } + for (String matchId : setOfMatchIds) { + strSetOfMatches += (strSetOfMatches == '' ? '' : ',') + matchId; + } + + return strSetOfMatches; + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/PotentialDuplicates.cls-meta.xml b/force-app/main/default/classes/PotentialDuplicates.cls-meta.xml new file mode 100644 index 00000000000..b1a915c9c6d --- /dev/null +++ b/force-app/main/default/classes/PotentialDuplicates.cls-meta.xml @@ -0,0 +1,5 @@ + + + 59.0 + Active + diff --git a/force-app/main/default/classes/PotentialDuplicates_TEST.cls b/force-app/main/default/classes/PotentialDuplicates_TEST.cls new file mode 100644 index 00000000000..6f81d20c876 --- /dev/null +++ b/force-app/main/default/classes/PotentialDuplicates_TEST.cls @@ -0,0 +1,57 @@ +@IsTest(IsParallel=false) +private with sharing class PotentialDuplicates_TEST { + + @IsTest + private static void shouldReturnNullWhenNoDuplicatesAreFound() { + Id recordId = UTIL_UnitTestData_TEST.mockId(Contact.getSObjectType()); + Map data = PotentialDuplicates.getDuplicates(recordId); + String setOfMatches = (String) data.get('setOfMatches'); + + List activeContactRules = [ + SELECT Id + from DuplicateRule + WHERE SObjectType = 'Contact' + AND isActive = TRUE + ]; + + if (activeContactRules.isEmpty()) { + System.assertEquals(null, setOfMatches, + 'PotentialDuplicates.getDuplicates() should return null if there are no active Duplicate Rules for Contact'); + } else { + System.assertEquals('', setOfMatches, 'There should be no duplicates'); + } + } + + @IsTest + private static void shouldReturnIdsWhenDuplicatesAreFound() { + List contactList = UTIL_UnitTestData_TEST.getContacts(3); + for (Contact c : contactList) { + c.FirstName = 'Test'; + c.LastName = 'LastName'; + c.Email = 'tester@example.com'; + } + insert contactList; + + List activeContactRules = [ + SELECT Id + from DuplicateRule + WHERE SObjectType = 'Contact' + AND isActive = TRUE + ]; + + Map data = PotentialDuplicates.getDuplicates(contactList[0].Id); + String setOfMatches = (String) data.get('setOfMatches'); + + if (activeContactRules.isEmpty()) { + System.assertEquals(null, setOfMatches, + 'PotentialDuplicates.getDuplicates() should return null if there are no active Duplicate Rules for Contact'); + } else if (sObjectType.Contact.fields.Name.isEncrypted()) { + // Duplicates will not be found if encryption is enabled / standard rules deactivated + System.assertEquals('', setOfMatches, 'No duplicate Ids should be returned if encryption is enabled'); + } else { + Integer numberOfMatches = setOfMatches.split(',').size(); + System.assertNotEquals('', setOfMatches, 'Duplicate Ids should be returned'); + System.assertEquals(2, numberOfMatches, 'There should be 2 duplicates returned'); + } + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/PotentialDuplicates_TEST.cls-meta.xml b/force-app/main/default/classes/PotentialDuplicates_TEST.cls-meta.xml new file mode 100644 index 00000000000..b1a915c9c6d --- /dev/null +++ b/force-app/main/default/classes/PotentialDuplicates_TEST.cls-meta.xml @@ -0,0 +1,5 @@ + + + 59.0 + Active + diff --git a/force-app/main/default/classes/RD2_PauseForm_CTRL.cls b/force-app/main/default/classes/RD2_PauseForm_CTRL.cls index 945a55d219d..72559fe6b3c 100644 --- a/force-app/main/default/classes/RD2_PauseForm_CTRL.cls +++ b/force-app/main/default/classes/RD2_PauseForm_CTRL.cls @@ -143,6 +143,29 @@ public with sharing class RD2_PauseForm_CTRL { UTIL_Permissions.canUpdate(rdObjectName, false); } + private static Boolean hasFieldReadAccess() { + Set fieldsToCheck = new Set{ + 'StartDate__c', + 'InstallmentFrequency__c', + 'npe03__Installment_Period__c', + 'npe03__Amount__c', + 'PaymentMethod__c', + 'Day_of_Month__c', + 'Status__c', + 'RecurringType__c', + 'EndDate__c' + }; + + for (String queryField:fieldsToCheck) { + if (!UTIL_Permissions.canRead('npe03__Recurring_Donation__c', + UTIL_Namespace.StrAllNSPrefix(queryField), false)) { + return false; + } + } + + return true; + } + /** * @description Query and Construct Recurring Donation Record * @param rdId Recurring Donation Id @@ -172,6 +195,10 @@ public with sharing class RD2_PauseForm_CTRL { queryFields.add('CurrencyIsoCode'); } + if (!hasFieldReadAccess()) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + RD2_QueryService queryService = new RD2_QueryService(); queryFields.add(queryService.getScheduleSubQuery()); @@ -256,6 +283,10 @@ public with sharing class RD2_PauseForm_CTRL { public static void savePause(String jsonPauseData) { Boolean hasScheduleChanged = false; try { + if (!hasAccess()) { + throw new UTIL_Permissions.InsufficientPermissionException(System.Label.commonAccessErrorMessage); + } + PauseData pause = (PauseData) JSON.deserialize(jsonPauseData, PauseData.class); RD2_RecurringDonation rd = getRecurringDonation(pause.rdId); @@ -296,9 +327,10 @@ public with sharing class RD2_PauseForm_CTRL { } private static UTIL_Http.Response pauseElevateRDsFor(PauseData pause) { - RD2_RecurringDonation rd = getRecurringDonation(pause.rdId); try { + RD2_RecurringDonation rd = getRecurringDonation(pause.rdId); + UTIL_Http.Response response; if (isPauseRemoved(pause)) { response = commitmentService.handleRemoveCommitmentPause(rd); diff --git a/force-app/main/default/classes/RD2_PauseForm_TEST.cls b/force-app/main/default/classes/RD2_PauseForm_TEST.cls index 9df37e336e0..1a02dc5644d 100644 --- a/force-app/main/default/classes/RD2_PauseForm_TEST.cls +++ b/force-app/main/default/classes/RD2_PauseForm_TEST.cls @@ -111,6 +111,7 @@ public with sharing class RD2_PauseForm_TEST { private static void shouldNotReturnPauseDataWhenUserDoesNotHaveCreateAndEditPermissions() { RD2_EnablementService_TEST.setRecurringDonations2Enabled(); RD2_ScheduleService.currentDate = START_DATE; + String errorMessage; npe03__Recurring_Donation__c rd = rdGateway.getRecords()[0]; RecurringDonationSchedule__c pauseSchedule = createPauseSchedule(rd.Id); @@ -119,13 +120,15 @@ public with sharing class RD2_PauseForm_TEST { System.runAs(readOnlyUser) { RD2_ScheduleService.currentDate = pauseSchedule.StartDate__c.addDays(1); - RD2_PauseForm_CTRL.PauseData pause = getPauseData(rd.Id); - System.assertEquals(false, pause.hasAccess, 'The user should not have access: ' + pause); - System.assertEquals(null, pause.isRDClosed, 'The Recurring Donation closed status should not be specified'); - System.assertEquals(null, pause.startDate, 'The Pause Start Date should not be set'); - System.assertEquals(null, pause.resumeAfterDate, 'The Pause Resume After Date should not be set'); - System.assertEquals(null, pause.pausedReason, 'The Paused Reason should not be initialized'); + RD2_PauseForm_CTRL.PauseData pause; + try { + pause = getPauseData(rd.Id); + } catch (AuraHandledException e) { + errorMessage = e.getMessage(); + } } + System.assertEquals(System.Label.commonAccessErrorMessage, errorMessage, + 'Message should be "' + System.Label.commonAccessErrorMessage + '"'); } /**** diff --git a/force-app/main/default/components/UTIL_InputField.component b/force-app/main/default/components/UTIL_InputField.component index a5375a6baf3..77d3d00fd2d 100644 --- a/force-app/main/default/components/UTIL_InputField.component +++ b/force-app/main/default/components/UTIL_InputField.component @@ -16,6 +16,7 @@ +
+
-
+
diff --git a/force-app/main/default/pages/CONV_Account_Conversion.page b/force-app/main/default/pages/CONV_Account_Conversion.page index 5909936759d..5b4798224bb 100644 --- a/force-app/main/default/pages/CONV_Account_Conversion.page +++ b/force-app/main/default/pages/CONV_Account_Conversion.page @@ -13,7 +13,7 @@ -
+
@@ -209,7 +209,7 @@
-
+