I wrote a VisualForce page that has two drop-down lists. When the user selects an item from the first list, it makes an Ajax call to populate the second list. Everything worked great.
I split the <apex:pageBlock>
into its own component, and now the Ajax call shows an error after making a selection in the first list:
Insufficient Privileges
You do not have the level of access necessary to perform the operation you requested. Please contact the owner of the record or your administrator if access is necessary.
The thing is, I am an administrator. This worked fine all in one VisualForce page, but now that it is split into a separate component, it fails. The debug log shows no failures. It does not tell me the specific object with the permission error. Based on some debug logging that I added, it appears to be failing before it even enters any Apex code. I think the failure is somewhere in the Ajax call but unfortunately none of the logging or error reporting tells me anything useful.
Unfortunately I cannot share the actual code/VF that causes this because it is proprietary to the customer, but here is a SSCCE:
VisualForce:
<apex:page id="pageId" controller="MyController">
<apex:form id="mainFormId">
<apex:sectionHeader subtitle="Custom Lookup Demo" title="Custom Lookup"/>
<c:MyComponent />
</apex:form>
</apex:page>
VisualForce Component:
<apex:component controller="MyController">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/JavaScript" />
<apex:pageBlock id="pgBId">
<apex:outputPanel id="SearchPanelId">
<table class="list" border="0" cellspacing="0" cellpadding="0" style="width: 44% !important">
<tr>
<td style="white-space:nowrap;">
<apex:selectList size="1" value="{!selectedObject}" id="selectedObjectId">
<apex:selectOptions value="{!SObjects}"/>
<apex:actionSupport event="onchange" action="{!populateFields}" rerender="selectfield,SearchPanelId" status="ajaxStatus"/>
</apex:selectList>
<apex:actionStatus id="ajaxStatus">
<apex:facet name="start"> <apex:image value="{!$MyImage}" height="16" width="16" /></apex:facet>
</apex:actionStatus>
</td>
<td>
<apex:selectList size="1" value="{!selectedField}" id="selectfield">
<apex:selectOption itemValue="" itemLabel="--Select--"/>
<apex:selectOptions value="{!fieldsForSelectedObject}"/>
</apex:selectList>
</td>
</tr>
</table>
</apex:outputPanel>
</apex:pageBlock>
</apex:component>
Controller
public with sharing class MyController {
private static final String SELECT_OBJECT = '--Select Object--';
public String selectedObject {get; set;}
public String selectedField {get; set;}
public String selectedValue {get; set;}
public List<SelectOption> allFieldsForSelectedObject {get; set;}
private Map<String, Schema.SObjectType> schemaCache {
get {
if (schemaCache == null) {
schemaCache = Schema.getGlobalDescribe();
}
return schemaCache;
}
private set{}
}
public List<SelectOption> sObjects {
get {
System.debug('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ Entering getSObjects()');
if (sObjects == null) {
System.debug('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ Populating sobjects');
List<SelectOption> objects = new List<SelectOption>();
objects.add(new SelectOption('', SELECT_OBJECT));
for (String objectKey : schemaCache.keySet()) {
Schema.SObjectType sObjectType = schemaCache.get(objectKey);
Schema.DescribeSObjectResult describe = sObjectType.getDescribe();
/* If the current user cannot query the object, do not bother displaying it in the list. */
if (describe.isQueryable()) {
objects.add(new SelectOption(describe.getName(), describe.getLabel()));
}
}
objects.sort();
sObjects = objects;
}
return sObjects;
}
private set{}
}
public MyController() {
populateFields();
}
/** Get the fields which will appear in the list of fields which can be queried. */
public List<SelectOption> getFieldsForSelectedObject() {
System.debug('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ Entering getFieldsForSelectedObject()');
List<SelectOption> fieldNames = new List<SelectOption>();
if ((selectedObject != null) && (selectedObject != '') && (selectedObject != SELECT_OBJECT)) {
// *************************************************************
// This IF block NEVER executes
// *************************************************************
System.debug('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ Entering main IF block');
Map<String, Schema.SObjectField> fieldMap = schemaCache.get(selectedObject).getDescribe().fields.getMap();
for (Schema.SObjectField field : fieldMap.values()) {
Schema.DescribeFieldResult describe = field.getDescribe();
System.debug('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ Checking field ' + describe.getName());
/* Only include this field if the current user can access it AND it can be used in a WHERE clause, otherwise the
* search function will break. */
if (describe.isAccessible() && describe.isFilterable()) {
System.debug('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ Adding field ' + describe.getName());
fieldNames.add(new SelectOption(describe.getName(), describe.getLabel()));
}
}
}
return fieldNames;
}
/** Repopulate the fields. */
public void populateFields() {
System.debug('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ Entering populateFields()');
allFieldsForSelectedObject = getFieldsForSelectedObject();
}
}
I found this related question that was similar but not applicable to this scenario. This is a dev sandbox using Unlimited Edition.
I compared the HTML from the component being embedded using the component tag and with it directly in the VF, and the only differences are there is one more layer of DIVs and one more layer of IDs in the generated JavaScript code. Seems to look okay.
If I copy and paste the component body into the VF page, I get two boxes and they both work. Then if I do nothing other than comment out the raw component markup in the VF, it stops working.
I wonder if maybe this has something to do with the JavaScript initialization? Maybe as a component some initialization logic is not running or not running correctly, and that causes the Ajax call not to work correctly which results in the permissions error?
This is bizarre.