Archives

SalesIO JQuery Mobile Force.com Mobile Application

SalesIO is Force.com JQuery Mobile Iphone App provides all the functionality that a busy sales rep will need to easily manage their day to day lifes. SalesIO provide:

  • Manage the basic Sales SObjects : Leads, Contacts, Accounts, Opportunities
  • Manage the basic Support SObject: Cases
  • Connect with customers using SMS and Email and Chatter
  • Graphs at your fingertips
  • Setup meetings and task
  • Organize notes and make todo lists











<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /><script type="text/javascript">// <![CDATA[

            var leadRecs = new Array(); 
            var contactRecs = new Array();
            var opportunityRecs = new Array();
            var accountRecs = new Array();
            var caseRecs = new Array();

            var restAPIClient = new forcetk.Client();
            restAPIClient.setSessionToken('{!$Api.Session_ID}');

            $j(document).ready(function() {
                regBtnClickHandlersLead();
                regBtnClickHandlersContact();
                regBtnClickHandlersAccount();
                regBtnClickHandlersOpportunity();
                regBtnClickHandlersChatter();
                regBtnClickHandlersEvent();
                regBtnClickHandlersCases();
                getAllLeads();
                getAllContacts();
                getAllAccounts();
                getAllOpportunities();
                getAllCases();
            });

           function getAllLeads(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, LastName, FirstName, Phone, MobilePhone, Email, Company, Description, LeadSource, Status, Industry from Lead where FirstName!=null order by FirstName limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showLeads(response.records); 
                                }); 
            }

            function getAllContacts(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, firstName, lastName, phone from Contact where FirstName!=null order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showContacts(response.records); 
                                }); 
            }

            function getAllAccounts(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, Name, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry, Phone, Fax, Website from Account where Name!=null order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showAccounts(response.records); 
                                }); 
            }

            function getAllOpportunities(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, Name, Description, StageName, Amount, Probability, ExpectedRevenue, CloseDate from Opportunity where Name!=null order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showOpportunities(response.records); 
                                }); 
            }

            function getAllCases(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, Status, Reason, Origin, Subject, Description from Case order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showCases(response.records); 
                                }); 
            }

            function showLeads(records)
            {
                 $j('#lList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { leadRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.FirstName+ ' '+this.LastName+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#lfName').val(leadRecs[this.id].FirstName);
                        $j('#llName').val(leadRecs[this.id].LastName);
                        $j('#lphone').val(leadRecs[this.id].Phone);
                        $j('#lemail').val(leadRecs[this.id].Email);
                        $j('#lcompany').val(leadRecs[this.id].Company);
                        $j('#leadId').val(leadRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#ldetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#lList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#lList:visible').listview('refresh');
            }

            function showContacts(records) {    
                $j('#cList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { contactRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.FirstName+ ' '+this.LastName+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#cfName').val(contactRecs[this.id].FirstName);
                        $j('#clName').val(contactRecs[this.id].LastName);
                        $j('#cphone').val(contactRecs[this.id].Phone);
                        $j('#contactId').val(contactRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#cdetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#cList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#cList:visible').listview('refresh');
            } 

             function showAccounts(records)
            {
                $j('#aList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { accountRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.Name+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#afName').val(accountRecs[this.id].Name);
                        $j('#aphone').val(accountRecs[this.id].Phone);
                        $j('#accountId').val(accountRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#adetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#aList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#aList:visible').listview('refresh');
            }     

             function showOpportunities(records)
            {
                $j('#oList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { opportunityRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,function() {
                    var newLi = $j('
	<li></li>

');
                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.Name+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#ofName').val(opportunityRecs[this.id].Name);
                        $j('#ostageName').val(opportunityRecs[this.id].StageName);
                        $j('#oamount').val(opportunityRecs[this.id].Amount);
                        $j('#oclosedate').val(opportunityRecs[this.id].CloseDate);
                        $j('#opportunityId').val(opportunityRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#odetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#oList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#oList:visible').listview('refresh');
            }

             function showCases(records)
            {
                $j('#csList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { caseRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.Subject + '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#csStatus').val(caseRecs[this.id].Status);
                        $j('#csReason').val(caseRecs[this.id].Reason);
                        $j('#csOrigin').val(caseRecs[this.id].Origin);
                        $j('#csSubject').val(caseRecs[this.id].Subject);
                        $j('#csDescription').val(caseRecs[this.id].Description);
                        $j('#caseId').val(caseRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#csdetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#csList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#csList:visible').listview('refresh');
            }

            function displayError(e){
                var error = JSON.parse(e.responseText);
                $j('#error').html(error[0].message);
            }

            function regBtnClickHandlersLead() {
                $j('#addlead').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#lfName').val('');
                    $j('#llName').val('');
                    $j('#lphone').val('');
                    $j('#lemail').val('');
                    $j('#lcompany').val('');
                    $j('#error').html('');
                    $j('#leadId').val('');
                    $j.mobile.changePage('#ldetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#savelead').click(function(e) {
                   addUpdateLead(e);
                });

                $j('#deletelead').click(function(e) {
                   deleteLead(e);
                });
            }

            function addUpdateLead(e){
                e.preventDefault();
                var lId = $j('#leadId').val();
                var lead = { FirstName : $j('#lfName').val(),
                                LastName : $j('#llName').val(),
                                Phone : $j('#lphone').val(),
                                Email : $j('#lemail').val(),
                                Company : $j('#lcompany').val()};
                if (lId === 'undefined' || lId === ''){
                    restAPIClient.create('Lead', lead, sucessCallbackLead, displayError); 
                } else {
                    restAPIClient.update('Lead', lId, lead, sucessCallbackLead, displayError); 
                }
            }

            function deleteLead(e){
                e.preventDefault();
                restAPIClient.del('Lead', $j('#leadId').val(), sucessCallbackLead, displayError);
            }

            function sucessCallbackLead(r){
                $j.mobile.changePage('#Lead', {changeHash: true});
            }

             function regBtnClickHandlersContact() {
                $j('#addcontact').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#cfName').val('');
                    $j('#clName').val('');
                    $j('#cphone').val('');
                    $j('#error').html('');
                    $j('#contactId').val('');
                    $j.mobile.changePage('#cdetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#savecontact').click(function(e) {
                   addUpdateContact(e);
                });

                $j('#deletecontact').click(function(e) {
                   deleteContact(e);
                });
            }

             function addUpdateContact(e){
                e.preventDefault();
                var cId = $j('#contactId').val();
                var contact = { FirstName : $j('#cfName').val(),
                                LastName : $j('#clName').val(),
                                Phone : $j('#cphone').val()};
                if (cId === 'undefined' || cId === ''){
                    restAPIClient.create('Contact', contact, sucessCallbackContact, displayError); 
                } else {
                    restAPIClient.update('Contact', cId, contact, sucessCallbackContact, displayError); 
                }
            }

            function deleteContact(e){
                e.preventDefault();
                restAPIClient.del('Contact', $j('#contactId').val(), sucessCallbackContact, displayError);
            }

            function sucessCallbackContact(r){
                $j.mobile.changePage('#Contact', {changeHash: true});
            }

             function regBtnClickHandlersAccount() {
                $j('#addaccount').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#afName').val('');
                    $j('#aphone').val('');
                    $j('#error').html('');
                    $j('#accountId').val('');
                    $j.mobile.changePage('#adetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#saveaccount').click(function(e) {
                   addUpdateAccount(e);
                });

                $j('#deleteaccount').click(function(e) {
                   deleteAccount(e);
                });
            }

            function addUpdateAccount(e){
                e.preventDefault();
                var aId = $j('#accountId').val();
                var account = { Name : $j('#afName').val(),
                                Phone : $j('#aphone').val()};
                if (aId === 'undefined' || aId === ''){
                    restAPIClient.create('Account', account, sucessCallbackAccount, displayError); 
                } else {
                    restAPIClient.update('Account', aId, account, sucessCallbackAccount, displayError); 
                }
            }

            function deleteAccount(e){
                e.preventDefault();
                restAPIClient.del('Account', $j('#accountId').val(), sucessCallbackAccount, displayError);
            }

            function sucessCallbackAccount(r){
                $j.mobile.changePage('#Account', {changeHash: true});
            }

            function regBtnClickHandlersOpportunity() {
                $j('#addopportunity').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#ofName').val('');
                    $j('#ostageName').val('');
                    $j('#oclosedate').val('');
                    $j('#oamount').val('');
                    $j('#error').html('');
                    $j('#opportunityId').val('');
                    $j.mobile.changePage('#odetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#saveopportunity').click(function(e) {
                   addUpdateOpportunity(e);
                });

                $j('#deleteopportunity').click(function(e) {
                   deleteOpportunity(e);
                });
            }

             function addUpdateOpportunity(e){
                e.preventDefault();
                var oId = $j('#opportunityId').val();
                var opportunity = { Name : $j('#ofName').val(),
                                StageName : $j('#ostageName').val(),
                                CloseDate : $j('#oclosedate').val(),
                                Amount : $j('#oamount').val()};
                if (oId === 'undefined' || oId === ''){
                    restAPIClient.create('Opportunity', opportunity, sucessCallbackOpportunity, displayError); 
                } else {
                    restAPIClient.update('Opportunity', oId, opportunity, sucessCallbackOpportunity, displayError); 
                }
            }

            function deleteOpportunity(e){
                e.preventDefault();
                restAPIClient.del('Opportunity', $j('#opportunityId').val(), sucessCallbackOpportunity, displayError);
            }

            function sucessCallbackOpportunity(r){
                $j.mobile.changePage('#Opportunity', {changeHash: true});
            }

             function regBtnClickHandlersCases() {
                $j('#addcase').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#csStatus').val('');
                    $j('#csReason').val('');
                    $j('#csOrigin').val('');
                    $j('#csSubject').val('');
                    $j('#csDescription').val('');
                    $j('#error').html('');
                    $j('#caseId').val('');
                    $j.mobile.changePage('#csdetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#savecase').click(function(e) {
                   addUpdateCase(e);
                });

                $j('#deletecase').click(function(e) {
                   deleteCase(e);
                });
            }

             function addUpdateCase(e){
                e.preventDefault();
                var csId = $j('#caseId').val();
                var caseObj = {    Status : $j('#csStatus').val(),
                                Reason : $j('#csReason').val(),
                                Origin : $j('#csOrigin').val(),
                                Subject : $j('#csSubject').val(),
                                Description : $j('#csDescription').val()};
                if (csId === 'undefined' || csId === ''){
                    restAPIClient.create('Case', caseObj, sucessCallbackCase, displayError); 
                } else {
                    restAPIClient.update('Case', csId, caseObj, sucessCallbackCase, displayError); 
                }
            }

            function deleteCase(e){
                e.preventDefault();
                restAPIClient.del('Case', $j('#caseId').val(), sucessCallbackCase, displayError);
            }

            function sucessCallbackCase(r){
                $j.mobile.changePage('#Case', {changeHash: true});
            }

            function regBtnClickHandlersChatter() {

                $j('#savechatter').click(function(e) {
                   addChatter(e);
                });
            }

            function addChatter(e){
                e.preventDefault();
                var chatter = { title : $j('#chattertitle').val(),
                                body : $j('#chatterbody').val(),
                                ParentId : $j('#cwhoId').val()};
                restAPIClient.create('FeedItem', chatter, sucessCallbackLead, displayError); 
            }

            function emailFunction()
            {
                sendMail();
            }

            function smsFunction()
            {
                sendSMS();
            }

            function regBtnClickHandlersEvent() {
                $j('#saveevent').click(function(e) {
                   addUpdateEvent(e);
                });
            }

             function addUpdateEvent(e){
                e.preventDefault();
                var event = { WhoId : $j('#ewhoId').val(),
                                Subject : $j('#esubject').val(),
                                StartDateTime : $j('#estartdatetime').val(),
                                EndDateTime : $j('#eenddatetime').val(),
                                Description : $j('#edescription').val()};

                restAPIClient.create('Event', event, sucessCallbackLead, displayError); 
            }

            function regBtnClickHandlersTask() {
                $j('#savetask').click(function(e) {
                   addUpdateTask(e);
                });
            }

             function addUpdateTask(e){
                e.preventDefault();
                var task =     { WhoId : $j('#twhoId').val(),
                                Subject : $j('#tsubject').val(),
                                Priority : $j('#tpriority').val(),
                                Status : $j('#tstatus').val(),
                                Description : $j('#tcomments').val()};

                restAPIClient.create('Task', task, sucessCallbackLead, displayError); 
            }

// ]]></script></pre>
<div class="jqm-demos jqm-demos-index" id="Lead" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="leadList" data-role="content"></div>
<!-- /content -->

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addlead" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<pre>
<!-- /page --></pre>
<div class="jqm-demos jqm-demos-index" id="ldetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="lfName">First Name:</label>
 <input id="lfName" type="text" name="lfName" /></div>
<div data-role="fieldcontain"><label for="llName">Last Name:</label>
 <input id="llName" type="text" name="llName" /></div>
<div data-role="fieldcontain"><label for="lcompany">Company:</label>
 <input id="lcompany" type="text" name="lcompany" /></div>
<div data-role="fieldcontain"><label for="lphone">Phone:</label>
 <input id="lphone" type="text" name="lphone" /></div>
<div data-role="fieldcontain"><label for="lemail">Email:</label>
 <input id="lemail" type="text" name="lemail" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="leadId" type="hidden" />
 <button class="save" id="savelead" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deletelead" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Contact" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="contactList" data-role="content"></div>
<!-- /content -->


<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addcontact" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<div class="jqm-demos jqm-demos-index" id="cdetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="cfName">First Name:</label>
 <input id="cfName" type="text" name="cfName" /></div>
<div data-role="fieldcontain"><label for="clName">Last Name:</label>
 <input id="clName" type="text" name="clName" /></div>
<div data-role="fieldcontain"><label for="cphone">Phone:</label>
 <input id="cphone" type="text" name="cphone" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="contactId" type="hidden" />
 <button class="save" id="savecontact" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deletecontact" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Account" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="accountList" data-role="content"></div>
<!-- /content -->


<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addaccount" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<div class="jqm-demos jqm-demos-index" id="adetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="afName">First Name:</label>
 <input id="afName" type="text" name="afName" /></div>
<div data-role="fieldcontain"><label for="aphone">Phone:</label>
 <input id="aphone" type="text" name="aphone" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="accountId" type="hidden" />
 <button class="save" id="saveaccount" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deleteaccount" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Opportunity" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="opportunityList" data-role="content"></div>
<!-- /content -->

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addopportunity" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<pre>
<!-- /page --></pre>
<div class="jqm-demos jqm-demos-index" id="odetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="ofName">Name:</label>
 <input id="ofName" type="text" name="ofName" /></div>
<div data-role="fieldcontain"><label for="ostageName">Stage Name:</label>
<select id="ostageName" name="ostageName"><option value="Closed Won">Closed Won</option><option value="Prospecting">Prospecting</option><option value="Qualification">Qualification</option><option value="Closed Lost">Closed Lost</option></select></div>
<div data-role="fieldcontain"><label for="oclosedate">Close Date:</label>
 <input id="oclosedate" type="date" name="oclosedate" data-role="datebox" data-options="{&quot;mode&quot;: &quot;calbox&quot;}" /></div>
<div data-role="fieldcontain"><label for="oamount">Amount:</label>
 <input id="oamount" type="text" name="oamount" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="opportunityId" type="hidden" />
 <button class="save" id="saveopportunity" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deleteopportunity" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Case" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="caseList" data-role="content"></div>
<!-- /content -->

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addcase" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<pre>
<!-- /page --></pre>
<div class="jqm-demos jqm-demos-index" id="csdetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="csStatus">Status:</label>
 <input id="csStatus" type="text" name="csStatus" /></div>
<div data-role="fieldcontain"><label for="csReason">Reason:</label>
 <input id="csReason" type="text" name="csReason" /></div>
<div data-role="fieldcontain"><label for="csOrigin">Origin:</label>
 <input id="csOrigin" type="text" name="csOrigin" /></div>
<div data-role="fieldcontain"><label for="csSubject">Subject:</label>
 <input id="csSubject" type="text" name="csSubject" /></div>
<div data-role="fieldcontain"><label for="csDescription">Description:</label>
 <input id="csDescription" type="text" name="csDescription" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="caseId" type="hidden" />

 <button class="save" id="savecase" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deletecase" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Chatter" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="chattertitle">Title:</label>
 <input id="chattertitle" type="text" name="chattertitle" /></div>
<div data-role="fieldcontain"><label for="chatterbody">Body:</label>
 <textarea id="chatterbody" name="chatterbody" rows="5"></textarea></div>
 <input id="cwhoId" type="hidden" name="cwhoId" value="{!userId}" />
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="savechatter" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button></div>

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" href="#NewChatter" data-prefetch="true" data-transition="none">New</a></li>
	<li><a href="#ViewChatter" data-prefetch="true" data-transition="none">View</a></li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<div class="jqm-demos jqm-demos-index" id="Email" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="emailAddress">Address:</label>
 <input id="emailAddress" type="text" name="emailAddress" value="{!emailAddress}" /></div>
<div data-role="fieldcontain"><label for="emailSubject">Title:</label>
 <input id="emailSubject" type="text" name="emailSubject" value="{!emailSubject}" /></div>
<div data-role="fieldcontain"><label for="emailBody">Body:</label>
 <textarea id="emailBody" name="emailBody" rows="5"></textarea></div>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="sendemail" onclick="emailFunction();" data-role="button" data-icon="check" data-inline="true" data-theme="b">Send</button>






</div>
</div>
<div class="jqm-demos jqm-demos-index" id="SMS" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="smsNumber">Number:</label>
 <input id="smsNumber" type="text" name="smsNumber" /></div>
<div data-role="fieldcontain"><label for="smsBody">Body:</label>
 <textarea id="smsBody" name="smsBody" rows="5"></textarea></div>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="sendsms" onclick="smsFunction();" data-role="button" data-icon="check" data-inline="true" data-theme="b">Send</button>



</div>
</div>
<div class="jqm-demos jqm-demos-index" id="Meeting" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="esubject">Subject:</label>
 <input id="esubject" type="text" name="esubject" /></div>
<div data-role="fieldcontain"><label for="estartdatetime">Start Date:</label>
 <input id="estartdatetime" type="datetime-local" name="estartdatetime" value="" data-clear-btn="true" /></div>
<div data-role="fieldcontain"><label for="eenddatetime">End Date:</label>
 <input id="eenddatetime" type="datetime-local" name="eenddatetime" value="" data-clear-btn="true" /></div>
<div data-role="fieldcontain"><label for="edescription">Description:</label>
 <textarea id="edescription" name="edescription" rows="5"></textarea></div>
 <input id="ewhoId" type="hidden" name="ewhoId" value="{!userId}" />
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="saveevent" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Task" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="addtask" data-role="button" data-icon="check" data-inline="true" data-theme="b">Send</button>

 <input id="twhoId" type="hidden" name="twhoId" value="{!userId}" /></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Todo" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content"><form>
<fieldset data-role="controlgroup"><legend>Vertical:</legend>
 <input id="checkbox-v-2a" type="checkbox" name="checkbox-v-2a" />
 <label for="checkbox-v-2a">One</label>
 <input id="checkbox-v-2b" type="checkbox" name="checkbox-v-2b" />
 <label for="checkbox-v-2b">Two</label>
 <input id="checkbox-v-2c" type="checkbox" name="checkbox-v-2c" />
 <label for="checkbox-v-2c">Three</label></fieldset>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="addtodo" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button></form></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Notes" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content"><form>
<div class="ui-hide-label" data-role="fieldcontain"><textarea id="textarea-16" style="height: 300px;" name="textarea-16"></textarea></div>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="addnotes" data-role="button" data-icon="check" data-inline="true" data-theme="b">SAVE</button></form></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Charts" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content"></div>
</div>
<pre>

</pre>
<div id="modal" data-role="page">
<div data-role="header" data-theme="e">
<h1>Dialog (modal)</h1>
</div>
<!-- /header -->
<div data-role="content" data-theme="d">
<h2>Modal</h2>
Content of Modal Goes Here</div>
<!-- /content -->
<div data-role="footer">
<h4>Page Footer</h4>
</div>
<!-- /footer --></div>
<pre>
<!-- /page modal -->

<script type="text/javascript">// <![CDATA[
      new Morris.Line({
  // ID of the element in which to draw the chart.
  element: 'myfirstchart',
  // Chart data records -- each entry in this array corresponds to a point on
  // the chart.
  data: [
    { year: '2008', value: 20 },
    { year: '2009', value: 10 },
    { year: '2010', value: 5 },
    { year: '2011', value: 5 },
    { year: '2012', value: 20 }
  ],
  // The name of the data record attribute that contains x-values.
  xkey: 'year',
  // A list of names of data record attributes that contain y-values.
  ykeys: ['value'],
  // Labels for the ykeys -- will be displayed when you hover over the
  // chart.
  labels: ['Value']
});

// ]]></script>


Clone on Github

git clone https://github.com/thysmichels/Sales-IO-JQuery-Mobile-Force.com-Mobile-App.git

Force.com FourSquare Visualforce Page

<apex:page showHeader="false" showChat="false" sidebar="false">
<html>
<head>
  <title>foursquare :: Explore Sample</title>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript" id="jquery"></script>
  <link href="{!URLFOR($Resource.ForceSquare, 'ForceSquare/leaflet.css')}" type="text/css" rel="stylesheet" />
  <script src="{!URLFOR($Resource.ForceSquare, 'ForceSquare/apisamples.js')}" type="text/javascript"></script>
  <script type="text/javascript" src="https://ss0.4sqi.net/scripts/third_party/jquery.ba-bbq-eddd4adf74d0c1310a401475178c57df.js"></script>
  <script src="{!URLFOR($Resource.ForceSquare, 'ForceSquare/leaflet.js')}" type="text/javascript"></script>
  <script src="{!URLFOR($Resource.ForceSquare, 'ForceSquare/wax.leaf.min.js')}" type="text/javascript"></script>
 
  <style type="text/css">
    html { height: 100%; }
    body { height: 100%; margin: 0; padding: 0; }
    /* Give our markers a background image */
    .leaflet-marker-icon {
      background: url(https://foursquare.com/img/pin-blue-transparent.png);
      padding: 6px;
      padding-bottom: 17px;
      top: -6px;
      left: -6px;
      }
  </style>
 
  <script type="text/javascript">
  
  //<![CDATA[
  var client_id = '<YOURCLIENTID>';
  var callback_url = 'https://cs17.salesforce.com/apex/FourSquarePage';

  /* Attempt to retrieve access token from URL. */
  if ($.bbq.getState('access_token')) {
    var token = $.bbq.getState('access_token');
    $.bbq.pushState({}, 2)
  } else if ($.bbq.getState('error')) {
  } else {
    /* Redirect for foursquare authentication. */
    window.location.href = 'https://foursquare.com/oauth2/authenticate?client_id=' + client_id
    + '&response_type=token&redirect_uri=' + callback_url;
  }  

  /* HTML 5 geolocation. */
  navigator.geolocation.getCurrentPosition(function(data) {
    var lat = data['coords']['latitude'];
    var lng = data['coords']['longitude'];
    /* Create map. */
    var map = new L.Map('map_canvas')
      .setView(new L.LatLng(lat, lng), 15);
    /**
     * This is a sample map url that you need to change.
     * Sign up at http://mapbox.com/foursquare for a custom map url.
     */
    var mapboxUrl = 'http://a.tiles.mapbox.com/v3/foursquare.map-b7qq4a62.jsonp';
    wax.tilejson(mapboxUrl, function(tilejson) {
      map.addLayer(new wax.leaf.connector(tilejson));
    });

    /* Query foursquare API for venue recommendations near the current location. */
    $.getJSON('https://api.foursquare.com/v2/venues/explore?ll=' + lat + ',' + lng + '&oauth_token=' + token, {}, function(data) {
      venues = data['response']['groups'][0]['items'];
      /* Place marker for each venue. */
      for (var i = 0; i < venues.length; i++) {
        /* Get marker's location */
        var latLng = new L.LatLng(
          venues[i]['venue']['location']['lat'], 
          venues[i]['venue']['location']['lng']
        );
        /* Build icon for each icon */
        var leafletIcon = L.Icon.extend({
          iconUrl: venues[i]['venue']['categories'][0]['icon'],
          shadowUrl: null,
          iconSize: new L.Point(32,32),
          iconAnchor: new L.Point(16, 41),
          popupAnchor: new L.Point(0, -51)
        });
        var icon = new leafletIcon();
        var marker = new L.Marker(latLng, {icon: icon})
          .bindPopup(venues[i]['venue']['name'], { closeButton: false })
          .on('mouseover', function(e) { this.openPopup(); })
          .on('mouseout', function(e) { this.closePopup(); });
        map.addLayer(marker);
      }       
    })
  })
  //]]>
  </script>
 
  
</head>
<body>
  <div style="width: 100%; height: 100%;" id="map_canvas"></div>
</body>
</html>
</apex:page>

Set in visualforce page:

var client_id = ‘YOURCLIENTAPPID’;
var callback_url = https://.salesforce.com/apex/FourSquarePage

Set in FourSquare App Setup Page:

Redirect URL https://.salesforce.com/apex/FourSquarePage

Visualforce Lookup Field

I have seen that allot of people are struggling with creating lookup field in Salesforce. It is very easy by just referencing the child relationship id in an apex:Inputfield. See examples below:

When using a standard controller in your page you can do the following:

<apex:page standardcontroller="Contact">
<apex:form>
<apex:inputField value="{!Contact.OwnerId}"/>
<apex:inputField value="{!Contact.AccountId}"/>
</apex:form>
</apex:page>



<apex:page standardcontroller="Account">
<apex:form>
<apex:inputField value="{!Account.OwnerId}"/>
<apex:inputField value="{!Account.RecordTypeId}"/>
<apex:inputField value="{!Account.ParentId}"/>
</apex:form>
</apex:page>

When using a custom controller in your page you can do the following:


<apex:page controller="UserReassignController">
<apex:form>
User From <apex:inputField label="UserFrom" id="UserFrom" value="{!User.OwnerId}" required="true"/>
User To <apex:inputField label="UserTo" id="UserTo" value="{!User.OwnerId}" required="true"/>
<apex:commandButton action="{!ReassignAllAccountContactOpportunities}" value="Reassign"/>
</apex:form>
</apex:page>


public with sharing class UserReassignController {

   private Account userFromOwnerId = new Account();
    private Account userToOwnerId = new Account();
  
    public Account UserFrom { get
    {
        return userFromOwnerId;
    } 
    set
    {
        userFromOwnerId = value;
    } 
    }

    public Account UserTo { get
    {
         return userToOwnerId;
    } 
    set
    {
        userToOwnerId = value;
    }
    }
}

Force.com Apex and Visualforce Best Practices

Triggers

  • There should only be one trigger for each object.
Apex Trigger Code

trigger ContactTrigger on Contact (after delete, after insert, after undelete, 
after update, before delete, before insert, before update) 
{
        ContactTriggerHandler handler = new ContactTriggerHandler(Trigger.isExecuting, Trigger.size);

        if(Trigger.isInsert &amp;&amp; Trigger.isBefore)
        {
		handler.OnBeforeInsert(Trigger.new);
	}
	else if(Trigger.isInsert &amp;&amp; Trigger.isAfter){
		handler.OnAfterInsert(Trigger.new);
		ContactTriggerHandler.OnAfterInsertAsync(Trigger.newMap.keySet());
	}

	else if(Trigger.isUpdate &amp;&amp; Trigger.isBefore){
		handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
	}
	else if(Trigger.isUpdate &amp;&amp; Trigger.isAfter){
		handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.newMap);
		ContactTriggerHandler.OnAfterUpdateAsync(Trigger.newMap.keySet());
	}

	else if(Trigger.isDelete &amp;&amp; Trigger.isBefore){
		handler.OnBeforeDelete(Trigger.old, Trigger.oldMap);
	}
	else if(Trigger.isDelete &amp;&amp; Trigger.isAfter){
		handler.OnAfterDelete(Trigger.old, Trigger.oldMap);
		ContactTriggerHandler.OnAfterDeleteAsync(Trigger.oldMap.keySet());
	}

	else if(Trigger.isUnDelete){
		handler.OnUndelete(Trigger.new);	
	}
}

Apex Class Code

public with sharing class ContactTriggerHandler {
	private boolean isHandlerExecuting = false;
	private integer BatchSize = 0;

	public ContactTriggerHandler(boolean isExecuting, integer size){
		isHandlerExecuting = isExecuting;
		BatchSize = size;
	}

	public void OnBeforeInsert(Contact[] newContacts){
		//Example usage
		for(Contact newContact : newContacts){
			if(newContact.Name == null){
				newContact.Name.addError('Missing Name Field');
			}
		}
	}

	public void OnAfterInsert(Contact[] newContacts){

	}

	@future public static void OnAfterInsertAsync(Set newContactIDs){
		//Example usage
		List newContacts = [select Id, Name from Contact where Id IN :newContactIDs];
	}

	public void OnBeforeUpdate(Contact[] oldContact, Contact[] updatedContacts, Map contactMap){
		//Example Map usage
		Map contacts = new Map( [select Id, FirstName, LastName, Email from Contact where Id IN :contactMap.keySet()] );
	}

	public void OnAfterUpdate(Contact[] oldContacts, Contact[] updatedContacts, Map contactMap){

	}

	@future public static void OnAfterUpdateAsync(Set updatedContactIDs){
		List updatedContacts = [select Id, Name from Contact where Id IN :updatedContactIDs];
	}

	public void OnBeforeDelete(Contact[] contactsToDelete, Map contactMap){

	}

	public void OnAfterDelete(Contact[] deletedContacts, Map contactMap){

	}

	@future public static void OnAfterDeleteAsync(Set deletedContactIDs){

	}

	public void OnUndelete(Contact[] restoredContacts){

	}

	public boolean IsTriggerContext{
		get{ return isHandlerExecuting;}
	}

	public boolean IsVisualforcePageContext{
		get{ return !IsTriggerContext;}
	}

	public boolean IsWebServiceContext{
		get{ return !IsTriggerContext;}
	}

	public boolean IsExecuteAnonymousContext{
		get{ return !IsTriggerContext;}
	}
}

  • Use Helper Classes


https://github.com/thysmichels/Force.com-Helper-Classes
https://github.com/thysmichels/base
http://code.google.com/p/apex-lang/

Dynamic Delete Batch Apex Class
global class dynamicDelete implements Database.Batchable{

   global final String Query;

   global dynamicDelete(String q)
   {
      Query=q;
   }

   global Database.QueryLocator start(Database.BatchableContext BC)
   {
      return Database.getQueryLocator(query);
   }

   global void execute(Database.BatchableContext BC, List scope)
   {
      delete scope;
   }
}

Scheduler for Batch Apex Delete

global class dynamicDeleteScheduler implements Schedulable{
   private static final String deleteLeads = 'Select id from Lead where Email like \'%test%\' limit 5000';
   private static final String deleteTasks = 'Select id from Task where WhoId IN (Select id from Contact where email like \'%test%\') limit 50000';
   private static final String deleteOpportunities = 'Select id from Opportunity where id in (Select OpportunityId from OpportunityContactRole where Contact.Email like \'%test%\') limit 50000';
   private static final String deleteCases = 'Select id from Case where ContactId IN (Select id from Contact where Email like \'%test%\') limit 50000';
   private static final String deleteTestAccounts = 'Select id from Account where Email__c like \'%test%\' limit 50000';
   private static final String deleteTestContacts = 'Select id from Contact where Email like \'%test%\' limit 50000';

   global void execute(SchedulableContext SC) 
   {     
      dynamicDelete deleteTask = new dynamicDelete(deleteTasks);
      Database.executebatch(deleteTask);

      dynamicDelete deleteCase = new dynamicDelete(deleteCases);
      Database.executebatch(deleteCase);

      dynamicDelete deleteOpportunity = new dynamicDelete(deleteOpportunities);
      Database.executebatch(deleteOpportunity);

      dynamicDelete deletecontact = new dynamicDelete(deleteTestContacts);
      Database.executebatch(deletecontact);

      dynamicDelete deleteaccount = new dynamicDelete(deleteTestAccounts); 
      Database.executebatch(deleteaccount);

   }
}
    Bulkify Triggers to process up to 200 records for each call. This will help you avoid hitting DML limit per process.
Bulk your DML operations with with List/Maps/Sets
private void bulkUpdateExample(Map updateContactOwner)
{
    List&lt;Contact&gt; listToHoldListOfContactsToBeUpdated = new List&lt;Contact&gt;();
    Id salesForceAdminId = [Select id from User where Name='Salesforce Administrator'].Id;
    for (Id contactIdToUpdateOwner : updateContactOwner.keySet())
    {
          updateContactOwner.get(contactIdToUpdateOwner).OwnerId = salesForceAdminId;
          //Add all records to a List/Map/Set to be updated outside the for loop
          listToHoldListOfContactsToBeUpdated.add(updateContactOwner.get(contactIdToUpdateOwner));
    }
    //Always update outside a for loop
    update listToHoldListOfContactsToBeUpdated;
}

Bulkify your Test classes with at least 200 records

@isTest(SeeAllData=false) static void testContactOwnerReassignmentSuccess()
{
  Account createAccount = new Account(Name='[TEST]');
  insert createAccount;

  List contactList = new List();
  for (int k = 0; k &lt; 200; k++)
      contactList.add(new Contact(FirstName='[Test]' + k, AccountId=createAccount.Id));

  Test.startTest();
  insert contactList;
  Test.stopTest();

  System.assert(200 == [Select count() from Contact where 
Owner.Name = 'Salesforce Administrator']);
}
    Use Collections in SOQL “WHERE” clauses to retrieve all records using a single query. Also use self, semi and anti joins:
 legalDocumentNames = new String[]
         {
            'Employment Application',
            'Release of Liability',
            'Non-Disclosure Agreement',
            '%Employment Agreement'
          }
List legalDocumentQuery = [select ContactId from Asset
where Product2.Name Like :legalDocumentNames];

Apex 

    Use @future annotation for methods that can be executed asynchronously
@future public static void OnAfterInsertAsync(Set newAssetIDs)
{
    new smbNurtureCampaign().insertCampaignMemebersToControlAndTestCampaign(newAssetIDs);
}
    Provide proper exception handling.
Apply try catch to all methods
public static User getSalesRepsForRouting(List getListActiveUser)
    {
        try
        {
          return getListActiveUser.size() == 1 ? getListActiveUser[0] : getListActiveUser[0];
        }
        catch(handlerException e)
        {
            Debugger.createDebugRecord(e, 'ACRoundRobinV5');
            sendErrorMail(e.getMessage());
            return null;
        }
    }    

Extend the Exception Class

      public class handlerException extends Exception{}

Catch Exception and create record in custom Debugger Object

public static void createDebugRecord(handlerException exceptionObject, String methodName)
     {
         Database.insert(new Debugger__c
            (   
                    Message__c = exceptionObject.getMessage(),
                    Line_Number__c = exceptionObject.getLineNumber(),
                    Stack_Trace__c = exceptionObject.getStackTraceString(),
                    Method_Name__c=methodName
            ), false);
     }
     public static void createDebugRecord(DMLException dmlexceptionObject, String methodName)
     {
         Database.insert(new Debugger__c
            (       
                    DML_Record_Id__c = dmlexceptionObject.getDmlId(0),
                    DML_Type__c = dmlexceptionObject.getTypeName(),
                    Message__c = dmlexceptionObject.getDmlMessage(0),
                    Line_Number__c = dmlexceptionObject.getDmlIndex(0),
                    Stack_Trace__c = dmlexceptionObject.getDmlStatusCode(0),
                    Method_Name__c=methodName
            ),false);
     }
    Prevent SOQL and SOSL injection attacks by using static queries, binding variables or the escapeSingleQuotes method.
        private static final String[] QUERY_SELECT_STATEMENT =
        new String[]{'select ',' from ', ' where Id = \''};

        String query = QUERY_SELECT_STATEMENT[0];
        for(Schema.DescribeFieldResult dfr : queryFields)
            query = query + dfr.getName() + ',';

        query = query.subString(0,query.length() - 1);
        query = query + QUERY_SELECT_STATEMENT[1];
        query = query + obj.getName();
        query = query + QUERY_SELECT_STATEMENT[2];
        query = query + theId + '\'';
    When querying large data sets, use a SOQL “for” loop
        for (Account accountToCheckAndUpdateAccountDetails : [Select BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry, ShippingStreet, ShippingCity, ShippingState, ShippingPostalCode, ShippingCountry, Phone, Fax, Website, Industry from Account])
        {
           .....
        }
        update accountToCheckAndUpdateAccountDetails;
    Use Apex Limits Methods to avoid hitting governor exceptions.
In the if statement we check if it is save to proceed without hitting governor limits 
List accountOwnerDetail = [SELECT OwnerId, Owner.Name, Owner.Profile.Name, Owner.isActive from Account];
if (accountOwnerDetail.size() + Limits.getDMLRows() &lt; Limits.getLimitDMLRows())
{
  ... 
}
    No SOQL or SOSL queries inside loops
Create parent-to-child and child-to-parent relationship queries outside for loops
Contact getContactWithAccount = Database.query('Select Name, Email,  Account.Name, Account.BillingStreet from Contact limit 1');
Account getAccountContactOpportunityInOneQuery = Database.query('Select Id, (Select Name from Account.Contacts), (Select Name from Account.Opportunities) from Account limit 1');

Tip: Update an Object without querying for it

List accountFieldsToBeUpdated = new List();
for (Contact updateAccountsFieldFromContact : [Select AccountId from Contact where OtherCity ='San Francisco'])
{ 
    //create new account object with the accountId from contact
     Account newAccountObject = new Account(Id=updateAccountsFieldFromContact.AccountId);
     newAccountObject.ShippingCity = 'SF';
     newAccountObject.BillingCity = 'SF';   
     accountFieldsToBeUpdated.add(newAccountObject);
}
update accountFieldsToBeUpdated ;
    Do not use hardcoded IDs: As ID's are unique and different between environments.
Don't use ID's rather get Id's by Name
        Opportunity getOpportunity = [Select Name from Opportunity where Owner.Id='00560000001S1eh' limit 1];

Rather do the following as your unit tests will fail in other environments

        Opportunity getOpportunity = [Select Name from Opportunity where Owner.Name='John Black' limit 1];

Visualforce

    Do not hardcode picklists in Visualforce pages; include them in the controller instead.
Picklist Controller Method
public List getFilterByRecordTypeList() 
    {
            List&lt;SelectOption&gt; op = new List&lt;SelectOption&gt;();
            Set&lt;Id&gt; setPPFamily = new Set&lt;Id&gt;();
            setPPFamily.clear();
            setPPFamily.add('All Opportunities');
            List&lt;AggregateResult&gt; aggregateResultsOfMotivatingProductFamily = [SELECT RecordType.Name FROM Opportunity Group By RecordType.Name];

            for (AggregateResult ppfamilyname : aggregateResultsOfMotivatingProductFamily)
            op.add(new SelectOption((String)ppfamilyname.get('RecordType.Name'),(String)ppfamilyname.get('RecordType.Name')));

            op.sort();
            return op;
    }

Calling Picklist Controller in Visualforce

&lt;apex:outputText style="color:#f8f8ff;font-weight:bold" value="Filter By Document Type"/&gt;
&lt;apex:toolbarGroup itemSeparator="line" location="left" id="toobarGroupForm2"&gt;
&lt;apex:selectList label="Filter by Record Type" value="{!FilterByRecordType}" size="1" required="false" &gt;
&lt;apex:actionSupport event="onchange" action="{!FilterByRecordTypeAction}" status="filterByRecordType" reRender="innerblock"/&gt;
&lt;apex:outputPanel style="color:#f8f8ff;font-weight:bold"&gt;
&lt;apex:actionStatus id="filterByRecordType" startText="Filtering your records..." stopText=""/&gt;
&lt;/apex:outputPanel&gt;
&lt;apex:selectOptions value="{!FilterByRecordTypeList}"/&gt;
&lt;/apex:selectList&gt;
&lt;/apex:toolbarGroup&gt;
    Javascript and CSS should be included as Static Resources allowing the browser to cache them.
Import your JS and CSS to the Static Resources as Zip of single files. Include CSS on top and JS at the bottom for faster loading
&lt;apex:page controller="YourCustomController" sidebar="true" cache="true" showChat="true" &gt;
&lt;head&gt;
&lt;apex:stylesheet value="{!URLFOR($Resource.jQuery1, '/css/ui-lightness/jquery-ui-1.8.23.custom.css')}" /&gt;
&lt;/head&gt;
&lt;body&gt;
....

<apex:includeScript value="{!URLFOR($Resource.jQuery1, '/js/jquery-1.8.0.min.js')}" />
<apex:includeScript value="{!URLFOR($Resource.jQuery1, '/js/jquery-ui-1.8.23.custom.min.js')}" />
</body>
</apex:page>
 

    Mark controller variables as “transient” if they are not needed between server calls. This will make your page load faster as it reduces the size of the View State.
Transient Example Visualforce Page
&lt;apex:page controller="ExampleController"&gt;
&lt;apex:form id="theForm"&gt;
&lt;apex:panelBar id="thePanel"&gt;
&lt;apex:panelbarItem label="Transient vs Non Transient"&gt;
&lt;b&gt;Transient Time: &lt;/b&gt; {!t1} &lt;br /&gt;
&lt;b&gt;Non Transient Time: &lt;/b&gt; {!t2}
&lt;/apex:panelbarItem&gt;
&lt;apex:actionPoller interval="5" enabled="true" rerender="thePanel" action="{!refresh}" /&gt;
&lt;/apex:panelBar&gt;
&lt;/apex:form&gt;
&lt;/apex:page&gt;

Transient Example Controller

public class ExampleController {

    public PageReference refresh() {
        return null;
    }

    DateTime t1;
    transient DateTime t2;

    public String getT1() {
        if (t1 == null) t1 = System.now();
        return '' + t1;
    }

    public String getT2() {
        if (t2 == null) t2 = System.now();
        return '' + t2;
    }
}
    Use the <repeat> tag to iterate over large collections.
&lt;apex:page standardController="Opportunity" id="thePage"&gt;
&lt;table id="EquipmentTable" border="1"&gt;
&lt;apex:repeat value="{!Opportunity}" var="line" id="theRepeat"&gt;
&lt;tr&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Id}" /&gt;&lt;/td&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Name}" /&gt;&lt;/td&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Probability}" /&gt;&lt;/td&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Amount}" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/apex:repeat&gt;
&lt;/table&gt;
&lt;/apex:page&gt;

Visualforce JQuery DatePicker

Below is the code to implement a JQuery DateTime Picker in your Visualforce page:

<apex:page controller="PipelineV6_1" sidebar="true" showChat="true" >
<head>
<apex:includeScript value="{!URLFOR($Resource.JQuery1_8, '/js/jquery-1.8.0.min.js')}" />
<apex:includeScript value="{!URLFOR($Resource.JQuery1_8, '/js/jquery-ui-1.8.23.custom.min.js')}" />
<apex:stylesheet value="{!URLFOR($Resource.JQuery1_8, '/css/ui-lightness/jquery-ui-1.8.23.custom.css')}" />
<script>
var j$ = jQuery.noConflict();

j$(document).ready(function($) {
j$('[id$=datepickerFrom]').datepicker("hide")
j$('[id$=datepickerTo]').datepicker("hide")
});
function callDatePickerFrom()
{
     j$('[id$=datepickerFrom]').datepicker({dateFormat: 'yy-mm-dd'})
}
function callDatePickerTo()
{
   j$('[id$=datepickerTo]').datepicker({dateFormat: 'yy-mm-dd'})
}
</script>

The body of my code looked like follows:

<apex:form id="theForm">
<apex:sectionHeader title="Pipeline Page for {!$User.FirstName}"/>
<apex:pageBlock id="innerblock" mode="edit">
<!-- The action function which calles the Apex function 'autosave' -->
<apex:toolbar id="theToolbar" style="background-color:#8d8d8d;background-image:none" rendered="true">
<apex:outputText style="color:#f8f8ff;font-weight:bold" value="Records to Display"/>
<apex:toolbarGroup itemSeparator="line" location="left" id="toobarGroupForm">
<apex:selectList label="Record Amount" value="{!ShowAmountOfRecords}" size="1" required="false" >
<apex:actionSupport event="onchange" action="{!AmountOfRecordsAction}" status="recordamountchange" reRender="innerblock" />
<apex:outputPanel style="color:#f8f8ff;font-weight:bold">
<apex:actionStatus id="recordamountchange" startText="Showing more records..." stopText=""/>
</apex:outputPanel>
<apex:selectOptions value="{!AmountOfRecordsList}"/>
</apex:selectList>
</apex:toolbarGroup>
<apex:outputText style="color:#f8f8ff;font-weight:bold" value="Filter By Document Type"/>
<apex:toolbarGroup itemSeparator="line" location="left" id="toobarGroupForm2">
<apex:selectList label="Filter by Record Type" value="{!FilterByRecordType}" size="1" required="false" >
<apex:actionSupport event="onchange" action="{!FilterByRecordTypeAction}" status="filterByRecordType" reRender="innerblock"/>
<apex:outputPanel style="color:#f8f8ff;font-weight:bold">
<apex:actionStatus id="filterByRecordType" startText="Filtering your records..." stopText=""/>
</apex:outputPanel>
<apex:selectOptions value="{!FilterByRecordTypeList}"/>
</apex:selectList>
</apex:toolbarGroup>
<apex:outputText style="color:#f8f8ff;font-weight:bold" value="From Date (YYYY-MM-DD) " />
<apex:inputText value="{!FromDate}" id="datepickerFrom"/>
<apex:outputText style="color:#f8f8ff;font-weight:bold" value="To Date (YYYY-MM-DD) " />
<apex:inputText value="{!ToDate}" id="datepickerTo"/>
</apex:toolbar>
</apex:pageBlock>
</apex:form>
</apex:page>

It will looks like follows:

jquerypicklist

Apex Code: Visualforce Google Maps Customer Mapping

Create a Visualforce page to show all your customers addresses on Google Maps. Note there is a limit on the amount of Map pins that are set thus I created a timeout of a few seconds so Google does not show me pop-up an error message.

<apex:page controller="getCustomerAddress" sidebar="false" showChat="true">
<html>
<head>
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> 
<script type="text/javascript"> 
 var arraddress= new Array();
 var arrids= new Array();
 var content=new Array();
 var arrnames= new Array();
 var i=0;
 
 var map;
 var geocoder;

function initialize() 
 {
 var mapDiv = document.getElementById('map-canvas');
 map = new google.maps.Map(mapDiv, 
 {
 zoom: 4,
 mapTypeId: google.maps.MapTypeId.ROADMAP 
 }
 );
 google.maps.event.addListenerOnce(map, 'idle', addMarkers);
 }
 
 function addMarkers() 
 {
 for (var i = 0; i < arraddress.length; i++) 
 {
 content[i] = arrnames[i]+' '+arraddress[i];
 geocodeAddress(arraddress[i],arrids[i],content[i]);
 }
 }
 
 function geocodeAddress(addds,id,content)
 {
 geocoder = new google.maps.Geocoder();
 
 geocoder.geocode( { 'address': addds}, function(results, status) 
 {
 if (status == google.maps.GeocoderStatus.OK) 
 {
 map.setCenter(results[0].geometry.location);
 
 var marker = new google.maps.Marker({map: map,position: results[0].geometry.location});
 var infowindow = new google.maps.InfoWindow({content: content});
google.maps.event.addListener(marker, 'mouseover', function() { infowindow.open(map,this);});
 
 google.maps.event.addListener(marker, 'mouseout', function() {infowindow.close();});
 google.maps.event.addListener(marker, 'click', function() { window.open('/'+id);});
 }
 else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) 
 { 
 setTimeout(function() {geocodeAddress(addds, id, content);}, 200);
 }
 else 
 {
 alert("Geocode was not successful for the following reason: " + status);
 }
 });
 }
 google.maps.event.addDomListener(window, 'load', initialize);
 
</script>
</head>
 <body style="font-family: Arial; border: 0 none;">
 <div id="map-canvas" style="width: 1500; height: 750px"></div>
 <apex:repeat value="{!Address}" var="a">
 <script>
 arraddress[i]= '{!a.BillingStreet}, {!a.BillingCity}, {!a.BillingPostalCode}, {!a.BillingCountry}';
 arrids[i]='{!a.id}';
 arrnames[i]='{!a.name}';
 i++;
 </script>
 </apex:repeat>
 </body>
 </html>
</apex:page>

Apex Code: Visualforce Google Maps Address mapping

Account: Chuck Norris ~ salesforce.com - Enterprise Edition

Below is the code to display Google map of the Billing Address of an Account. Add the Visualforce page to your page layout.

Account: Chuck Norris ~ salesforce.com - Enterprise Edition

<apex:page standardController="Account">
<head>
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> 
<script type="text/javascript

$(document).ready(function() 
{
var myOptions = 
 {
 zoom: 15,
 mapTypeId: google.maps.MapTypeId.ROADMAP,
 mapTypeControl: false
 }
var map;
 var marker;
var geocoder = new google.maps.Geocoder();
 var address = "{!Account.BillingStreet}, " + "{!Account.BillingCity}, " + "{!Account.BillingPostalCode}, " + "{!Account.BillingCountry}";
var infowindow = new google.maps.InfoWindow({
 content: "<b>{!Account.Name}</b><br>{!Account.BillingStreet}<br>{!Account.BillingCity}, {!Account.BillingPostalCode}<br>{!Account.BillingCountry}"
 });
geocoder.geocode( { address: address}, function(results, status) {
 if (status == google.maps.GeocoderStatus.OK && results.length) {
 if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
//create map
 map = new google.maps.Map(document.getElementById("map"), myOptions);
//center map
 map.setCenter(results[0].geometry.location);
//create marker
 marker = new google.maps.Marker({
 position: results[0].geometry.location,
 map: map,
 title: "{!Account.Name}"
 });
//add listeners
 google.maps.event.addListener(marker, 'click', function() {
 infowindow.open(map,marker);
 });
 google.maps.event.addListener(infowindow, 'closeclick', function() {
 map.setCenter(marker.getPosition()); 
 });
}
} else {
 $('#map').css({'height' : '15px'});
 $('#map').html("{!Account.Name}'s billing address could not be found, confirm the address is correct.");
 resizeIframe();
 }
 });
function resizeIframe() {
 var me = window.name;
 if (me) {
 var iframes = parent.document.getElementsByName(me);
 if (iframes && iframes.length == 1) {
 height = document.body.offsetHeight;
 iframes[0].style.height = height + "px";
 }
 }
 }
});
</script>
<style>
#map {
 font-family: Arial;
 font-size:12px;
 line-height:normal !important;
 height:250px;
 background:transparent;
}
</style>
</head>
<body>
<div id="map"></div> 
</body> 
</apex:page>

Visualforce Standard vs Custom Apex Chart

The Visualforce chart below is key to understand how customization and development work in Salesforce.

Standard Behavior Custom Behavior
Standard Look and Feel Application Framework and Default UI (Page Layout)Example: Standard Opportunity page with a default page layout. Page Layouts and Custom Apex ClassesExample: Standard Opportunity page that validates the opportunity stage.
Custom Look and Feel Visualforce pages with standard controllersExample: Visualforce page containing opportunity information Visualforce pages with custom Apex ControllersExample:Opportunity search portal to search for specific opportunities in Salesforce.
%d bloggers like this: