1

I'm completely new to SalesForce and have inherited a report that's not working. Please excuse any incorrect terminology, since I'm learning about all this as I go. The report has three prompts: states, years, and members. All dropdowns are supposed to populate with data returned from functions in an APEX class. State, which populates from a picklist, and years, which is populated with a loop, work fine. Members, which populates from a SQL query, returns nothing. If I run the report without any prompts selected (which should return an unfiltered list of results from a SQL query), it also returns nothing. Both of the SQL queries return data when I execute them directly in the query editor in the developer console, but they return nothing when called from the APEX functions.

Here's the initialization code from the Lightning controller:

doInit: function (component, event, helper) {
        var action = component.get('c.getTrcAccounts');
        action.setCallback(this, function (response) {
            var state = response.getState();
            if (state === 'SUCCESS' && component.isValid()) {
                component.set('v.trcAccList', response.getReturnValue());
            }
            helper.getLocationState(component, event);
            helper.getYear(component, event);
        });
        $A.enqueueAction(action);
    },

Here are the two helper functions referenced in that code:

getLocationState: function (component, event) {
        var action = component.get('c.getLocationState');
        action.setCallback(this, function (response) {
            var state = response.getState();
            if (state === 'SUCCESS') {
                component.set('v.LocationStateList', response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    },
    getYear: function (component, event) {
        var action = component.get('c.yearsOptions');
        action.setCallback(this, function (response) {
            var state = response.getState();
            if (state === 'SUCCESS') {
                component.set('v.LocationYearList', response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    }

Here is the code from the APEX class that returns the data for those three prompts:

Global class DataTableLocations {
@AuraEnabled
    Global static List<TRC_Account__c> getTrcAccounts(){
        set<string> trcAccountSet = new set<string>();
        List<TRC_Account__c> traccList = new List<TRC_Account__c>();
        for(TRC_Account__c trcacc : [SELECT Id, Name from TRC_Account__c WHERE TRC_Member__c = True order by Name limit 50000]){
            if(!trcAccountSet.contains(trcacc.Name)){
                trcAccountSet.add(trcacc.Name);
                traccList.add(trcacc);
            }
        }
        if(traccList.size()>0){
            return traccList;
        }
        else{
            return null;
        }   
    }
    @AuraEnabled
    Global static List<string> getLocationState(){
        List<string> options = new List<string>();
        //options.add(new SelectOption('SelectAll', 'Select All'));
        for( Schema.PicklistEntry f : Location__c.Physical_Address_State__c.getDescribe().getPicklistValues()) {
            options.add(f.getValue());
        } 
        return options;
    }
    
    @AuraEnabled
    Global static List<string> yearsOptions() {
        List<string> options = new List<string>();
        date OldDate= date.today().addYears(-18);
        integer oldyear=OldDate.year();
        for( integer i=0; i<19 ;i++) {
            options.add(string.valueOf(oldyear));
            oldyear++;
        } 
        return options;
    }
}

If I run SELECT Id, Name from TRC_Account__c WHERE TRC_Member__c = True order by Name limit 50000 directly in the query editor window in the developer console, I get 7 results. However, if I output the response.getReturnValue() for getTrcAccounts in the doInit function, it's null.

Any help is greatly appreciated, as we're in a bit of a time crunch in conjunction with a site redesign. I'm told these reports were working at one point, but no one knows when they stopped working, and we inherited this code from a different company that did the original development. Thank you!

UPDATE:

In case it helps, this is the code in the lightning app that I think is used on the public page:

<aura:application extends="ltng:outApp" access="GLOBAL" implements="ltng:allowGuestAccess">
    <aura:dependency resource="c:SearchBinReceiptsByYear"/>
</aura:application>
user3562286
  • 151
  • 2
  • 13

1 Answers1

1

Edit

Right, it's a public page, it's called "Salesforce Sites". It's exposed to whole world without having to log in. These have special security in place because most of the time you don't want to expose data like that. At best you'd display contact us form, maybe some documents to download, product catalog... It's all very locked down, default is to ban everything and then admin decides what's allowed. It's bit unusual to have a Visualforce page + Aura component but ok, it happens.

You (and any other internal user) can see the results if you'd access this page from within salesforce. Something like https://mydomain.my.salesforce.com/apex/SearchBinReceiptsByYear and for you the page will work fine, "just" not outside of salesforce.

When exposed like that on the web - there's no logged in user. There's special "[Site Name] Guest User", you can see them if you search "Sites" in Setup. It has a special profile, also with [Site Name] in it. And nasty thing is - it doesn't show on the list of Users or Profiles.

Your code broke when Salesforce (auto)activated a critical update. Probably this one: https://releasenotes.docs.salesforce.com/en-us/spring20/release-notes/rn_networks_secure_perms_guests.htm There are some good resources on the net if you Google "Secure Object Permissions for Guest Users", for example https://katiekodes.com/salesforce-spring-20-guest-user/

Ask your system administrator colleague or read up a bit about sharing rules.

You'll have to go to Setup -> Sharing Rules. There's a checkbox that caused your stuff to break and you can't untick it.

enter image description here

Scroll down to your TRC Account object and hit "New". You'll need to create something like this, but with your criteria (TRC Member equals true)

enter image description here

Save, wait a bit (it might take a while to recalculate the sharing, you'll get an email) and try the page.

If it still doesn't work you'll have to check the Guest user's profile, it might need permissions to Read TRC Accounts and their Name field.

If it's Salesforce Sites - try this to find it: https://help.salesforce.com/articleView?id=000334554&type=1&mode=1

If it's a Customer Portal, Community, Digital Experience (they renamed the product few times) - try with https://help.salesforce.com/articleView?id=sf.rss_config_guest_user_profile.htm&type=5


Original answer

It looks like it's running OK because accounts (members?) are fetched first and in that fetch's callback (what to do when data comes back from server) you have helper.getLocationState, helper.getYear. And you wrote that these populate OK. It's not the best performance code but it should get the job done.

In no specific order...

Does the whole thing work OK for sysadmins? Or is it broken for everybody? If it works for sysadmins it might be something to do with sharing, your sysadmin should know (Setup -> Sharing settings is where you control who can see what. Maybe "mortals" are not allowed to see any data? Typically sysadmins bypass it. As a quick & dirty test you can modify the class definition to global without sharing class DataTableLocations but it's a really ugly hack.

What happens if you open DeveloperConsole (upper right corner) while running this component, do you see any errors in the logs? What happens if in the console you go Debug -> Open ExecuteAnonymous and run this piece of code:

System.debug(DataTableLocations.getTrcAccounts());

enter image description here

Does it return something? Throw error?

You can go to Setup -> Debug Mode, tick the checkbox next to your user and save. This slows the system down a bit but lets you debug the javascript better. You can then sprinkle some debugger; or console.log statements in the source code and view what happens in your browser's console (Ctrl+Shift+J in Chrome, Ctrl+Shift+I in firefox). For example

action.setCallback(this, function (response) {
    var state = response.getState();
    debugger;
    console.log(state);
    console.log(component.isValid());
    console.table(response.getReturnValue());
    if (state === 'SUCCESS' && component.isValid()) {
        component.set('v.trcAccList', response.getReturnValue());
    }
    console.log(component.get('v.trcAccList'));
    debugger;
    helper.getLocationState(component, event);
    helper.getYear(component, event);
});

How's the trcAccList variable actually used in the "cmp" file, in the HTML-like file? Maybe it's being set all right and contains 7 records but it's not displayed right?

eyescream
  • 18,088
  • 2
  • 34
  • 46
  • Thanks so much for all the suggestions. I had originally added some debugging statements in the controller code like your last suggestion, and that's how I determined that no results were being returned from the APEX function. The values returned were: state = SUCCESS component.isValid() = true response.getReturnValue() = null I just tried running System.debug(DataTableLocations.getTrcAccounts()); in Open Execute Anonymous, and it ran successfully. The log shows 7 rows returned from the SOQL which match what I see when I run the query directly in the query editor. – user3562286 Mar 31 '21 at 14:52
  • I've read other things about sharing but am at a loss for how to update the APEX class - if I try to make any changes to that file, I get a pop up that says "Deployment Failed Can't alter metadata in an active org." I've read that this is because you can't alter the APEX files in the production environment and need to do it in sandbox then deploy to production; however, we don't have a sandbox environment and all of this code was inherited as is from the previous company. I've updated the original post to include some extra info about the page that includes the lightning app, if that helps. – user3562286 Mar 31 '21 at 14:55
  • Answer updated, give it a go. If it works - we sorted out the top of the page, you'd have to repeat the steps for whatever this "report" is doing. Every object it queries, every field will have to be exposed to the guest user... – eyescream Mar 31 '21 at 17:30
  • In Setup -> Sandboxes you should be able to create new sandbox, edit the code in there and then probably easiest for you is to use "change sets" to push this to your production org. Marking the whole class as "without sharing" is terrible security practice though, I'd say last resort – eyescream Mar 31 '21 at 17:32
  • In Setup -> Debug logs you can enable tracking on this special Mr "[Site Name] Guest User", it's bit annoying to search but "he" will be on the list. Might help with checking if data's visible now. – eyescream Mar 31 '21 at 17:37
  • 1
    Problem solved I think, I can't thank you enough! I truly appreciate you taking the time to outline the multiple options with such detailed explanations. Updating the sharing settings did the trick - adding a rule for TRC account got the member list to populate in the prompt, and then I had to add additional rules for two other objects (bin and location) to get the report to return results. – user3562286 Mar 31 '21 at 23:09
  • Glad I could help. There's more to SF security. Is this page really meant to be public, without any login? Maybe edit your question to hide the url ;) Read up about customer and partner communities (renamed to digital experiences). And look around https://salesforce.stackexchange.com/, you might get faster help with non-coding stuff on this sister site. – eyescream Apr 01 '21 at 07:31