Define Your Own Custom Editors
You can define your own Custom Editors. With them, you can create a VisualForce page to use in place of the standard Salesforce edit page. The steps for creating a Custom Editor for Quotes are detailed in the example below. Use the Quote example below as a model for creating your own Custom Editors.
To ensure the VisualForce page for custom editors renders fields in the same order as they appear in the FieldFX Back Office page layout, you must set the Custom FX Setting
|
Example: Define a Custom Editor for Quotes
In this example, we will show you how to define a Custom Editor for quotes.
Feature Impact
Once we set up this Custom Editor, these lookups on the Quotes tab work as follows.
Customer Lookup
Setting | Value | Impact |
---|---|---|
Lookup Filter |
Impact: Only customer accounts that aren’t marked archived display available for selection. |
|
Fields for Available Records |
|
The listed fields display for customers in search results. |
Fields Analyzed for Keyword Searches |
|
Keyword searches analyze the names of customers. |
Sort Order of Available Records |
|
Customers sort in alphabetical order by name. |
Office Lookup
Setting | Value | Impact |
---|---|---|
Lookup Filter |
Impact: Only office accounts that aren’t marked archived display available for selection. |
|
Fields for Available Records |
|
The listed fields display for offices in search results. |
Fields Analyzed for Keyword Searches |
|
Keyword searches analyze the names of offices. |
Sort Order of Available Records |
|
Offices sort in alphabetical order by name. |
Price Book Lookup
Setting | Value | Impact |
---|---|---|
Lookup Filter |
Impact: Only active price books linked to the job’s customer, office, or segment display available for selection. |
|
Fields for Available Records |
|
The listed fields display for price books in search results. |
Fields Analyzed for Keyword Searches |
|
Keyword searches analyze the name, customer, office, and segment of price books. |
Sort Order of Available Records |
|
Price books sort in alphabetical order by name. |
Setup Instructions
Prerequisites
Create a New FXEditorSearcher Visualforce Page
-
From Setup, enter
pages
in the Quick Find box, then select Visualforce Pages. -
Click New.
-
In Label, enter
FXEditorSearcher
. -
Copy the following code:
Click to expand
<apex:page controller="FX5.FXEditorSearchController" title="{!SearcherTitle}" showHeader="false" sideBar="false" id="pg" lightningstylesheets="true"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <apex:sectionHeader title="{!SearcherTitle}" rendered="true"/> <apex:form > <apex:outputpanel id="page" layout="block" style="margin:5px;padding:5px;padding-top:2px;"> <apex:actionregion > <apex:outputpanel id="top" layout="block" style="margin:5px;padding:5px;padding-top:2px;"> <apex:outputlabel value="Keyword" style="font-weight:Bold;padding-right:5px;" for="txtSearch" /> <apex:inputtext id="txtSearch" value="{!KeywordString}" /> <span style="padding-left:5px"><apex:commandbutton id="btnGo" onclick="EnableMe('btnGo', false);EnableMe('txtSearch', false)" value="Go" status="searchstatus" action="{!Search}" rerender="searchResults" oncomplete="EnableMe('btnGo', true);EnableMe('txtSearch', true);setsearchfocus();"></apex:commandbutton></span> <apex:outputlabel style="padding-left:25px" value="Wildcard '*' search is not supported." /> </apex:outputpanel> <div id="clearresults" style="display: none;"> <apex:outputpanel id="clearsearch"> < <apex:commandlink value="Clear Search Results" rerender="searchResults" onclick="j$('[id$=\'txtSearch\']').val('');EnableMe('btnGo', false);EnableMe('txtSearch', false)" status="searchstatus" action="{!Search}" oncomplete="EnableMe('btnGo', true);EnableMe('txtSearch', true);setsearchfocus();" /> <br /> <br /> <br /> </apex:outputpanel> </div> <apex:outputpanel id="pnlSearchResults" style="margin:5px;height:350px;overflow-Y:auto;" layout="block"> <apex:actionstatus id="searchstatus"> <apex:facet name="start"> <apex:outputpanel > <apex:image value="/img/loading32.gif" style="height: 15px" /> </apex:outputpanel> </apex:facet> <apex:facet name="stop"> <apex:pageblock id="searchResults"> <apex:pageblocktable value="{!results}" var="a" id="tblResults"> <apex:column > <apex:facet name="header"> <apex:outputpanel >Name</apex:outputpanel> </apex:facet> <apex:outputlink value="javascript:top.window.opener.lookupPick2('{!FormTag}','{!TextBox}_lkid','{!TextBox}','{!a.Id}', '{!a['Name']}', false)" rendered="{!NOT(ISNULL(a.Id))}">{!a["Name"]}</apex:outputlink> </apex:column> apex:repeat value="{!DisplayColumns}" var="f"> <apex:column > <apex:facet name="header"> <apex:outputpanel >{!f.Label}</apex:outputpanel> </apex:facet> <apex:outputlabel value="{!a[f.FieldPath]}" rendered="{!NOT(ISNULL(a[f.FieldPath]))}" /> </apex:column> /apex:repeat> </apex:pageblocktable> </apex:pageblock> </apex:facet> </apex:actionstatus> </apex:outputpanel> </apex:actionregion> </apex:outputpanel> </apex:form> <script type="text/javascript"> var j$ = jQuery.noConflict(); j$(document).ready(function() { j$("[id$='txtSearch']:first").focus(); var defaultsearch = getUrlParameter('lksrch'); if (defaultsearch != undefined) { if (j$("[id$='txtSearch']").val() != defaultsearch) { j$("[id$='txtSearch']").val(defaultsearch); j$("[id$='btnGo']").trigger("click"); } } }); j$("[id$='txtSearch']").keydown(function(e) { if (e.keyCode == 13) { e.preventDefault(); j$("[id$='btnGo']").trigger("click"); } }); function getUrlParameter(sParam) { var sPageURL = decodeURIComponent(window.location.search.substring(1)), sURLVariables = sPageURL.split('&'), sParameterName, i; for (i = 0; i < sURLVariables.length; i++) { sParameterName = sURLVariables[i].split('='); if (sParameterName[0] === sParam) { return sParameterName[1] === undefined ? true : sParameterName[1]; } } }; function EnableMe(objid, enabled) { if (objid != undefined && enabled != undefined) { if (enabled == false) { j$('input[id$=' + objid + ']').attr('disabled', 'disabled'); j$('input[id$=' + objid + ']').addClass('btnDisabled'); j$("#clearresults").hide(); } else { j$('input[id$=' + objid + ']').removeAttr('disabled', 'disabled'); j$('input[id$=' + objid + ']').removeClass('btnDisabled'); } } } function setsearchfocus() { j$('input[id$=txtSearch]').focus(); if (j$("[id$='txtSearch']").val() != '') { j$("#clearresults").show(); } else { j$("#clearresults").hide(); } } </script> </apex:page>
-
Replace the code on the Visualforce Markup tab by pasting over it with the code you copied.
-
Click the Version Settings tab.
-
Click -- Select to Add Installed Package -- and select "FieldFX Base Package".
-
Click Save.
Add the FXQuoteEditor
-
From Setup, enter
pages
in the Quick Find box, then select Visualforce Pages. -
Click New.
-
In Label, enter
FXQuoteEditor
. -
Copy the following code:
Click to expand
<apex:page standardcontroller="FX5__Quote__c" extensions="FX5.FXEditorController,FX5.UtilityGetInstanceUrl" sidebar="false" tabstyle="FX5__Quote__c" lightningstylesheets="true"> <apex:sectionheader title="Edit {!EntityTypeDisplayLabel}" rendered="{!NOT(IsInsert)}" /> <apex:sectionheader title="New {!EntityTypeDisplayLabel}" rendered="{!IsInsert}" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <apex:includescript value="https://code.jquery.com/jquery-2.2.4.min.js" /> <script src="https://cdn.fieldfx.com/{!$Api.Session_ID}/{!FXNamespaceWithoutUnderscores + '__'}/customeditor/LATEST/main.js?sitePrefix={!$Site.Prefix}&siteDomain={!$Site.Domain}&instanceUrl={!instanceUrl}" type="text/javascript"></script> <apex:form id="editorForm"> <apex:pagemessages /> <apex:pageblock mode="edit" id="pbMain"> <apex:pageblockbuttons > <apex:commandbutton action="{!save}" value="Save" id="cmdSave" /> <apex:commandbutton action="{!saveAndNew}" value="Save & New" id="cmdSaveAndNew" /> <apex:commandbutton action="{!doCancel}" value="Cancel" immediate="true" /> </apex:pageblockbuttons> <apex:repeat value="{!LayoutSections}" var="s"> <apex:pageblocksection columns="{!s.columns}" title="{!s.heading}" collapsible="false"> <apex:repeat value="{!s.fields}" var="f"> <apex:inputfield value="{!EntityObj[f.fieldName]}" styleclass="fxdatafield {!f.fieldName}" required="{!f.required}" html-data-fieldname="{!f.fieldName}" /> </apex:repeat> </apex:pageblocksection> </apex:repeat> </apex:pageblock> <apex:inputhidden value="{!LayoutJsonString}" /> </apex:form> <script type="text/javascript"> var customSearch = '{!FXNamespace}CustomerAccount__c,{!FXNamespace}Office__c,{!FXNamespace}Price_Book__c'; var searchAttrs = `{ "{!FXNamespace}CustomerAccount__c" : { "QueryFilter" : "RecordType.DeveloperName='Customer' AND {!FXNamespace}IsArchived__c=false", "QueryFields" : "Id,Name", "KeywordSearchFields" : "Name", "OrderBy" : "Name" }, "{!FXNamespace}Office__c" : { "QueryFilter" : "RecordType.DeveloperName='Office' AND {!FXNamespace}IsArchived__c=false", "QueryFields" : "Id,Name", "KeywordSearchFields" : "Name", "OrderBy" : "Name" }, "{!FXNamespace}Price_Book__c" : { "QueryFilter" : "{!FXNamespace}Is_Active__c=true AND ({!FXNamespace}Account__c='@@this.{!FXNamespace}CustomerAccount__c@@' OR {!FXNamespace}Global_Price_Book__c=true)", "QueryFields" : "Name,{!FXNamespace}Account__r.Name,{!FXNamespace}Office__r.Name,{!FXNamespace}Segment__c", "KeywordSearchFields" : "Name,{!FXNamespace}Account__r.Name,{!FXNamespace}Office__r.Name,{!FXNamespace}Segment__c", "OrderBy" : "Name" } }`; function openLookup(baseURL, width, modified, searchParam) { var j$ = jQuery.noConflict(); var originalbaseURL = baseURL; var originalwidth = width; var originalmodified = modified; var originalsearchParam = searchParam; var entityType = '{!EntityType}'; var entityId = '{!EntityId}'; var originatorId = '{!ReturnUrlObjectId}'; var lookupType = getParameterByName('lktp', baseURL); var lookupCustomFieldId = getParameterByName('lknm', baseURL); var ctrl = j$('[Id$="' + lookupCustomFieldId + '"]')[0]; var lookupCustomField = j$(ctrl).data('fieldname'); if (modified == '1') baseURL = baseURL + searchParam; var pIndex = baseURL.indexOf('?'); var params = baseURL.substring(pIndex); var isCustomLookup = customSearch.indexOf(lookupCustomField) !== -1; console.log('isCustomLookup for ' + lookupCustomField + ' : ' + isCustomLookup); if (isCustomLookup == true) { var urlArr = baseURL.split("&"); var txtId = ''; if (urlArr.length > 2) { urlArr = urlArr[1].split('='); txtId = urlArr[1]; } params = params.replace('&lknm=' + encodeURIComponent(lookupCustomFieldId), '&lknm=' + lookupCustomField); console.log('params: ' + params); var jsonSubjectEntity = getContextEntity(); // Following is the url of Custom Lookup page. You need to change that accordingly baseURL = "/apex/FXEditorSearcher" + params; baseURL += "&txt=" + txtId; baseURL += "&subjectType={!EntityType}&subjectId={!EntityId}&originatorId={!ReturnUrlObjectId}"; baseURL += "&subjectEntity=" + escapeUTF(jsonSubjectEntity); // Following is the id of apex:form control "myForm". You need to change that accordingly baseURL = baseURL + "&frm=" + escapeUTF("{!$Component.editorForm}"); if (modified == '1') { baseURL = baseURL + "&lksearch=" + searchParam; } baseURL = baseURL + "&searchAttrs=" + searchAttrs; openPopup(baseURL, "lookup", 350, 480, "width=" + width + ",height=480,toolbar=no,status=no,directories=no,menubar=no,resizable=yes,scrollable=no", true); } else { if (modified == '1') originalbaseURL = originalbaseURL + originalsearchParam; openPopup(originalbaseURL, "lookup", 350, 480, "width=" + originalwidth + ",height=480,toolbar=no,status=no,directories=no,menubar=no,resizable=yes,scrollable=no", true); } } </script> </apex:page>
-
Replace the code on the Visualforce Markup tab by pasting over it with the code you copied.
-
Click the Version Settings tab.
-
Click -- Select to Add Installed Package -- and select "FieldFX Base Package".
-
Click Save.
-
Continue with Configure the FXQuoteEditor.
Configure the FXQuoteEditor
-
From Setup, enter
object
in the Quick Find box, then select Objects. -
Click Quote.
-
Under Buttons, Links, and Actions,
Even though this image is for the CrewPlanningEditor, the Edit option is located in the same location.
-
Find these rows and complete these actions:
-
Clone
-
Edit
-
New
-
-
In Override With, select "Visualforce", select "FXQuoteEditor".
-
Click Save.
-
-
Continue with Add a RESTEndPoint Remote Site Setting.
Add a RESTEndPoint Remote Site Setting
-
Open the Quote tab.
-
Open a quote.
-
Click Edit.
-
If the FXQuoteEditor displays, proceed to step grant permission to users.
-
If an error message displays, continue with the next step.
-
-
Copy the domain listed in the error message.
Example 1. Sample domain to copyUse your customized my domain URL that is specific to your org instead of your instance specific URL. You can find your my domain here.
Don’t use instanced URLs when logging in to Salesforce with code or as a user. When your org is moved to another Salesforce instance, code using the instanced URL breaks. If you find instanced URLs in your code, replace them with your My Domain login URL or the default Salesforce login URL. See Log In to Salesforce with Code for more information.
-
From Setup, enter
remote
in the Quick Find box, then select Remote Site Settings -
Click New Remote Site.
-
In Remote Site Name, enter
RESTEndpoint
. -
In Remote Site URL, enter the listed domain you copied earlier.
The Remote Site URL field is case-sensitive. Make sure the URL you enter uses the correct capitalization.
-
Click Active.
-
Click Save.
-
Continue with Grant Permission to Use the Added Visualforce Pages.
Grant Permission to Use the Added Visualforce Pages
-
From Setup, enter
permission
in the Quick Find box, then select Permission Sets. -
Open a permission set for Visualforce pages.
-
Under Apps, click Visualforce Page Access.
-
Click Edit.
-
Add "FXEditorSearcher" and "FXQuoteEditor" to Enabled Visualforce Pages.
-
Click Save.
-
Repeat these steps for other permission sets as needed.