Tag Archives: Apex

How to write batch apex class in Salesforce?

What is Batch Apex Class:

The class that implements Database.Batchable interface can be executed as a Batch Apex Class. Batch jobs can be programmatically invoked at runtime using Apex.

Batch Apex Governor Limits:

  • Up to five queued or active batch jobs are allowed for Apex.
  • A user can have up to 50 query cursors open at a time. For example, if 50 cursors are open and a client application still logged in as the same user attempts to open a new one, the oldest of the 50 cursors is released. Note that this limit is different for the batch Apexstart method, which can have up to 15 query cursors open at a time per user. The other batch Apex methods have the higher limit of 50 cursors.
  • Cursor limits for different Force.com features are tracked separately. For example, you can have 50 Apex query cursors, 50 batch cursors, and 50 Visualforce cursors open at the same time.
  • A maximum of 50 million records can be returned in the Database.QueryLocator object. If more than 50 million records are returned, the batch job is immediately terminated and marked as Failed.
  • If the start method returns a QueryLocator, the optional scope parameter of Database.executeBatch can have a maximum value of 2,000. If set to a higher value, Salesforce chunks the records returned by the QueryLocator into smaller batches of up to 2,000 records. If the start method returns an iterable, the scope parameter value has no upper limit; however, if you use a very high number, you may run into other limits.
  • If no size is specified with the optional scope parameter of Database.executeBatch, Salesforce chunks the records returned by the start method into batches of 200, and then passes each batch to the execute method.Apex governor limits are reset for each execution of execute.
  • The start, execute, and finish methods can implement up to 10 callouts each.
  • Batch executions are limited to 10 callouts per method execution.
  • The maximum number of batch executions is 250,000 per 24 hours.
  • Only one batch Apex job’s start method can run at a time in an organization. Batch jobs that haven’t started yet remain in the queue until they’re started. Note that this limit doesn’t cause any batch job to fail and execute methods of batch Apex jobs still run in parallel if more than one job is running.

Why we use Batch Class:

We use batch Apex to build complex DML operations for bulk records or long-running processes that run on thousands of records on the Force.com platform.
The Database.Batchable interface contains three methods that must be implemented.

start method:

global Database.QueryLocater start(Database.BatchableContext bc) {} 
  • The start method is called at the beginning of a batch Apex job.
  • This method is Used to collect the records or objects to pass to the interface method execute.
  • This method returns either a Database.QueryLocator object or an Iterable that contains the records or objects passed into the job.
  • Query executed in the start method will return maximum 5,00,00000 records in a transaction.

execute method:

global void execute(Database.BatchableContext bc , List scope) {} 
  • The execute method is called for each batch of records passed to the method.
  • This method is used for do all the processing for data.
  • This method takes the following:
    • A reference of Database.BatchableContext object.
    • A list of sObject records.
  • Batches of records are not guaranteed to execute in the order they are received from the start method.

finish method:

global void finish(Database.BatchableContext bc) {}
  • The finish method is called after all batches are processed.
  • This method is used to send confirmation emails or execute post-processing operations.
  • This method takes only one argument a reference of Database.BatchableContext object.
  • This method is called when all the batches are processed.

Each execution of a batch Apex job is considered a discrete transaction. For example, a batch Apex job that contains 1,000 records and is executed without the optional scope parameter from Database.executeBatch is considered five transactions of 200 records each. The Apex governor limits are reset for each transaction. If the first transaction succeeds but the second fails, the database updates made in the first transaction are not rolled back.

Here is an example of batch class.

Sample Code:

global class batchAccountUpdate implements Database.Batchable<sobject> {
 
    global Database.QueryLocator start(Database.BatchableContext bc){
     
        String query = 'SELECT Id, Name FROM Account';
        return Database.getQueryLocator(query);
    }
     
    global void execute(Database.BatchableContext bc, List<account> scope) {
     
        for(Account a : scope) {
            a.Name = a.Name + 'Updated';
        }
        update scope;
    } 
     
    global void finish(Database.BatchableContext bc) {
     
    }
}

If you want to notify admin on successful run, then in that case, create an email using this code below and send email by adding this code on finish method.

global void finish(Database.BatchableContext bc){
	Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
	mail.setToAddresses(new String[] {email});
	mail.setReplyTo('info@biswajeetsamal.com');
	mail.setSenderDisplayName('Account Batch Processing');
	mail.setSubject('Account Batch Process Completed');
	mail.setPlainTextBody('Account Batch Process has completed');
	Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}

Call the Batch Class:

batchAccountUpdate batchAcc = new batchAccountUpdate();
database.executebatch(batchAcc, 10);

Note: If batch size is not mentioned it is 200 by default.

Restrict multiple approval for a single user to a single record in Salesforce

I had a requirement to restrict multiple approval for a single user to a single record. There were multiple steps of approval process and the approver may be in different queue or as a manager for a user.

So, I implemented it using a trigger. In this article I’ll demonstrate how to implement it.
Here Application__c is the object, on which 3 steps approval process has implemented.
A custom check box field Check_Approver__c has set value to true, in all 3 steps field update action.

In below trigger I filtered the ProcessInstanceStep object records with current user Id in ActorId and the object Id (here object Id means record Id) in ProcessInstance.TargetObjectId.

  • If an User will approve or reject a record, then the SOQL query will return a single record.
  • If the SOQL query return a record, then trigger will throw an error message.

Trigger source code:

trigger ApplicationTrigger on Application__c (before Update)
{
    if(trigger.isUpdate){

        Id currentUserId = UserInfo.getUserId();
        for(Application__c sf: trigger.new){   
                  
            if(sf.Check_Approver__c == true){
              
                List<processinstancestep> existingApprovals = [SELECT ActorId
                                    FROM ProcessInstanceStep WHERE ProcessInstance.TargetObjectId = :sf.Id
                                    AND (StepStatus = 'Approved' OR StepStatus = 'Rejected')
                                    AND ActorId = :currentUserId];
                                      
                if(existingApprovals != null){
                  
                    if(existingApprovals.size() > 0){

                        sf.addError('You have already approved or rejeted the record.');
                    }
                }                  
             }
         }                         
    }
}

Hard Delete record using Apex in Salesforce

We can hard delete record or list of records using emptyRecycleBin() function in apex. Pass the record or record list to emptyRecycleBin() to delete it from Recycle Bin.

Contact con = new Contact(Id = '09k110000O5abc');
Database.emptyRecycleBin(con);

Note: The DML operation datatbase.emptyRecycleBin() is limited to 200 items.

Date Literals in Salesforce SOQL

Date Literal Range Example
YESTERDAY Starts 12:00:00 the day before and continues for 24 hours. SELECT Id FROM Account WHERE CreatedDate =
YESTERDAY
TODAY Starts 12:00:00 of the current day and continues for 24 hours. SELECT Id FROM Account WHERE CreatedDate >
TODAY
TOMORROW Starts 12:00:00 after the current day and continues for 24 hours. SELECT Id FROM Opportunity WHERE CloseDate =
TOMORROW
LAST_WEEK Starts 12:00:00 on the first day of the week before the most recent first day
of the week and continues for seven full days. First day of the week is determined
by your locale.
SELECT Id FROM Account WHERE CreatedDate >
LAST_WEEK
THIS_WEEK Starts 12:00:00 on the most recent first day of the week before the current day
and continues for seven full days. First day of the week is determined by your
locale.
SELECT Id FROM Account WHERE CreatedDate <
THIS_WEEK
NEXT_WEEK Starts 12:00:00 on the most recent first day of the week after the current day
and continues for seven full days. First day of the week is determined by your
locale.
SELECT Id FROM Opportunity WHERE CloseDate =
NEXT_WEEK
LAST_MONTH Starts 12:00:00 on the first day of the month before the current day and
continues for all the days of that month.
SELECT Id FROM Opportunity WHERE CloseDate >
LAST_MONTH
THIS_MONTH Starts 12:00:00 on the first day of the month that the current day is in and
continues for all the days of that month.
SELECT Id FROM Account WHERE CreatedDate <
THIS_MONTH
NEXT_MONTH Starts 12:00:00 on the first day of the month after the month that the current
day is in and continues for all the days of that month.
SELECT Id FROM Opportunity WHERE CloseDate =
NEXT_MONTH
LAST_90_DAYS Starts 12:00:00 of the current day and continues for the last 90 days. SELECT Id FROM Account WHERE CreatedDate =
LAST_90_DAYS
NEXT_90_DAYS Starts 12:00:00 of the current day and continues for the next 90 days. SELECT Id FROM Opportunity WHERE CloseDate >
NEXT_90_DAYS
LAST_N_DAYS:n For the number n provided, starts 12:00:00 of the current day and continues for the last n days. SELECT Id FROM Account WHERE CreatedDate =
LAST_N_DAYS:365
NEXT_N_DAYS:n For the number n provided, starts 12:00:00 of the current day and continues for the next n days. SELECT Id FROM Opportunity WHERE CloseDate >
NEXT_N_DAYS:15
NEXT_N_WEEKS: n For the number n provided, starts 12:00:00 of the first day of the next week and continues for the next n weeks. SELECT Id FROM Opportunity WHERE CloseDate >
NEXT_N_WEEKS:4
LAST_N_WEEKS:n For the number n provided, starts 12:00:00 of the last day of the previous week and continues for the last n weeks. SELECT Id FROM Account WHERE CreatedDate = LAST_N_WEEKS:52
NEXT_N_MONTHS:n For the number n provided, starts 12:00:00 of the first day of the next month and continues for the next n months. SELECT Id FROM Opportunity WHERE CloseDate >
NEXT_N_MONTHS:2
LAST_N_MONTHS:n For the number n provided, starts 12:00:00 of the last day of the previous month and continues for the last n months. SELECT Id FROM Account WHERE CreatedDate =
LAST_N_MONTHS:12
THIS_QUARTER Starts 12:00:00 of the current quarter and continues to the end of the current
quarter.
SELECT Id FROM Account WHERE CreatedDate =
THIS_QUARTER
LAST_QUARTER Starts 12:00:00 of the previous quarter and continues to the end of that
quarter.
SELECT Id FROM Account WHERE CreatedDate >
LAST_QUARTER
NEXT_QUARTER Starts 12:00:00 of the next quarter and continues to the end of that
quarter.
SELECT Id FROM Account WHERE CreatedDate <
NEXT_QUARTER
NEXT_N_QUARTERS:n Starts 12:00:00 of the next quarter and continues to the end of the nth quarter. SELECT Id FROM Account WHERE CreatedDate <
NEXT_N_QUARTERS:2
LAST_N_QUARTERS:n Starts 12:00:00 of the previous quarter and continues to the end of the previous nth quarter. SELECT Id FROM Account WHERE CreatedDate >
LAST_N_QUARTERS:2
THIS_YEAR Starts 12:00:00 on January 1 of the current year and continues through the end
of December 31 of the current year.
SELECT Id FROM Opportunity WHERE CloseDate =
THIS_YEAR
LAST_YEAR Starts 12:00:00 on January 1 of the previous year and continues through the end
of December 31 of that year.
SELECT Id FROM Opportunity WHERE CloseDate >
LAST_YEAR
NEXT_YEAR Starts 12:00:00 on January 1 of the following year and continues through the
end of December 31 of that year.
SELECT Id FROM Opportunity WHERE CloseDate <
NEXT_YEAR
NEXT_N_YEARS:n Starts 12:00:00 on January 1 of the following year and continues through the end of December 31 of the nth year. SELECT Id FROM Opportunity WHERE CloseDate <
NEXT_N_YEARS:5
LAST_N_YEARS:n Starts 12:00:00 on January 1 of the previous year and continues through the end of December 31 of the previous nth year. SELECT Id FROM Opportunity WHERE CloseDate >
LAST_N_YEARS:5
THIS_FISCAL_QUARTER Starts 12:00:00 on the first day of the current fiscal quarter and continues
through the end of the last day of the fiscal quarter. The fiscal year is defined in
the company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Account WHERE CreatedDate =
THIS_FISCAL_QUARTER
LAST_FISCAL_QUARTER Starts 12:00:00 on the first day of the last fiscal quarter and continues
through the end of the last day of that fiscal quarter. The fiscal year is defined
in the company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Account WHERE CreatedDate >
LAST_FISCAL_QUARTER
NEXT_FISCAL_QUARTER Starts 12:00:00 on the first day of the next fiscal quarter and continues
through the end of the last day of that fiscal quarter. The fiscal year is defined
in the company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Account WHERE CreatedDate <
NEXT_FISCAL_QUARTER
NEXT_N_FISCAL_​QUARTERS:n Starts 12:00:00 on the first day of the next fiscal quarter and continues through the end of the last day of the nth fiscal quarter. The fiscal year is defined in the company profile under Setup atCompany Profile | Fiscal Year. SELECT Id FROM Account WHERE CreatedDate <
NEXT_N_FISCAL_QUARTERS:6
LAST_N_FISCAL_​QUARTERS:n Starts 12:00:00 on the first day of the last fiscal quarter and continues
through the end of the last day of the previous
nth fiscal
quarter. The fiscal year is defined in the company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Account WHERE CreatedDate >
LAST_N_FISCAL_QUARTERS:6
THIS_FISCAL_YEAR Starts 12:00:00 on the first day of the current fiscal year and continues
through the end of the last day of the fiscal year. The fiscal year is defined in
the company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Opportunity WHERE CloseDate =
THIS_FISCAL_YEAR
LAST_FISCAL_YEAR Starts 12:00:00 on the first day of the last fiscal year and continues through
the end of the last day of that fiscal year. The fiscal year is defined in the
company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Opportunity WHERE CloseDate >
LAST_FISCAL_YEAR
NEXT_FISCAL_YEAR Starts 12:00:00 on the first day of the next fiscal year and continues through
the end of the last day of that fiscal year. The fiscal year is defined in the
company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Opportunity WHERE CloseDate <
NEXT_FISCAL_YEAR
NEXT_N_FISCAL_​YEARS:n Starts 12:00:00 on the first day of the next fiscal year and continues through
the end of the last day of the
nth fiscal year. The fiscal year
is defined in the company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Opportunity WHERE CloseDate <
NEXT_N_FISCAL_YEARS:3
LAST_N_FISCAL_​YEARS:n Starts 12:00:00 on the first day of the last fiscal year and continues through
the end of the last day of the previous
nth fiscal year. The
fiscal year is defined in the company profile under Setup at Company Profile | Fiscal Year.
SELECT Id FROM Opportunity WHERE CloseDate >
LAST_N_FISCAL_YEARS:3

How to retain and display the checkbox selection in visualforce paging?

Here is the example of Visualforce Page Pagination with checkbox selection, on Account object.
Custom Controller: PagingWithCheckBox.cls

public with sharing class PagingWithCheckBox{
 
    //Properties
    public integer PageSize {get;set;}
    public integer PageNumber {get;set;}
    public integer RecordCount {get;set;}
    public List<accountwrapper> objAWList {get;set;}
    private Set<id> selectedAccountIds;
    public boolean displayPopup {get; set;}  
    public List<account> objSelectedAccounts {get; set;}  
     
    //Constructor
    public PagingWithCheckBox() {
        PageSize = 10;
        PageNumber = 1;   
        RecordCount = [Select Count() From Account];
        selectedAccountIds = new Set<id>();
        objAWList = new List<accountwrapper>();
        objSelectedAccounts  = new List<account>();
         
    }
     
    //Close Popup
    public void closePopup() {        
        displayPopup = false;    
    }     
     
    //Show Popup
    public void showPopup() {  
        setSelectedAccounts();      
        displayPopup = true;    
        objSelectedAccounts  = GetSelectedAccounts();
    }
     
    //Get Selected Accounts
    public Integer getSelectedCount(){
        return this.selectedAccountIds.size();
    }
     
    //Get Selected Accounts Detail
    public List<account> GetSelectedAccounts(){    
        List<account> objAccList = [Select Id, Name, Industry, Website, AccountNumber 
                                    From Account  Where ID IN : selectedAccountIds];       
        return objAccList;
    }
     
     
    private void setSelectedAccounts()
    {
        for(AccountWrapper item: objAWList)
        {
            if(item.IsSelected)
            {    
                if(!this.selectedAccountIds.contains(item.AccountId))
                {
                    this.selectedAccountIds.add(item.AccountId);
                }
            }
            else
            {
                if(this.selectedAccountIds.contains(item.AccountId))
                {
                    this.selectedAccountIds.remove(item.AccountId);
                }
            }
        }        
    }
     
    public integer PageIndex {
        get { return (PageNumber - 1); }
    }    
     
    public integer PageCount {
        get { return getPageCount(); }
    }
     
    public integer Offset {
        get { return (PageSize * PageIndex); }
    }
     
    public List<accountwrapper> GetAccountList() {
        objAWList = new List<accountwrapper>();
        try {
            Account[] objAccList = [Select Id, Name, Industry, Website, AccountNumber
                                    From Account order by Name 
                                    Limit :PageSize
                                    offset :Offset];
                                     
             for(Account acc : objAccList){
                AccountWrapper objAW = new AccountWrapper();
                objAW.AccountId = acc.Id;
                objAW.AccountName = acc.Name;
                objAW.Industry = acc.Industry;
                objAW.Website = acc.Website;
                objAW.AccountNumber = acc.AccountNumber;
                if(this.selectedAccountIds.contains(acc.Id)){
                    objAW.IsSelected = true;
                }
                else{
                    objAW.IsSelected = false;
                }
                objAWList.Add(objAW);
           }             
        return objAWList;
    }
    catch (QueryException e) {
        ApexPages.addMessages(e);   
        return null;
        }
    }
     
    public integer LNumber {
        get { return RecordCount == 0 ? 0 : (Offset + 1); }
    }
 
    public integer UNumber {
        get { 
            integer iUNum = ((LNumber + PageSize) - 1);
            return (iUnum > RecordCount) ? RecordCount : iUNum; 
        }
    }
     
    public boolean AllowMoveNext {
        get{ return ((PageIndex + 1) < PageCount); } } public boolean AllowMovePrev { get{ return (PageIndex > 0); }
    }
     
    public void Prev() {
        setSelectedAccounts();
        PageNumber--;
 
        if (PageNumber <= 0) { PageNumber = 1; } } public void Next() { setSelectedAccounts(); PageNumber++; if (PageNumber > PageCount) {
            PageNumber = PageCount;
        }
    }
     
    public void Last() {
        setSelectedAccounts();
        PageNumber = PageCount; 
    }
 
    public void First() {
        setSelectedAccounts();
        PageNumber = 1;
    }
     
    private integer getPageCount() {
        integer iPageCount = 1;
 
        if (RecordCount != 0 && PageSize != 0) {
            iPageCount = (RecordCount/PageSize) + ((Math.mod(RecordCount, PageSize)) > 0 ? 1 : 0);
        }
        return iPageCount;
    }
     
    //Wrapper Class for Account
    public class AccountWrapper
    {
        public Id AccountId {get;set;}
        public String AccountName {get;set;}
        public String Industry {get;set;}
        public String Website {get;set;}
        public String AccountNumber {get;set;}
        public Boolean IsSelected{get;set;}
        public AccountWrapper()
        {
        }
    }    
}

Visualforce Page: PagingWithCheckBox.page

<apex:page doctype="html-5.0" title="Sample Visualforce Paging With CheckBox Example" controller="PagingWithCheckBox" tabstyle="Account" sidebar="false" readonly="true" cache="false">
    <apex:sectionheader title="Account" subtitle="Paging Accounts"></apex:sectionheader>
    <apex:form>
        <apex:pageblock title="View Selected Account" id="idSearchProduct">
            <apex:commandbutton value="View Selected Accounts" action="{!showPopup}" rerender="tstpopup"></apex:commandbutton>
        </apex:pageblock>
     
     <apex:outputpanel id="tstpopup">
        <apex:outputpanel styleclass="popupBackground" layout="block" rendered="{!displayPopUp}"></apex:outputpanel>
            <apex:outputpanel styleclass="custPopup" layout="block" rendered="{!displayPopUp}">
                <apex:pageblock>
               <apex:pageblocktable id="pbSelectedAccounts" value="{!objSelectedAccounts}" var="a">
                            <apex:column value="{!a.Name}" headervalue="Account Name"></apex:column>
                            <apex:column value="{!a.AccountNumber}" headervalue="Account Number"></apex:column>
                            <apex:column value="{!a.Industry}" headervalue="Industry"></apex:column>
                            <apex:column value="{!a.Website}" headervalue="Website"></apex:column>
                        </apex:pageblocktable>
               
               <center>
                <apex:commandbutton value="Close Popup" action="{!closePopup}" rerender="tstpopup"></apex:commandbutton>
                               </center>
                </apex:pageblock>
            </apex:outputpanel>
        </apex:outputpanel>
         
        <apex:pageblock title="Accounts">
            <apex:pageblocksection columns="1">
                <apex:facet name="body">
                    <apex:outputpanel layout="none">
                        <apex:pageblocktable id="pbAccounts" value="{!AccountList}" var="a">
                            <apex:column>
                                <apex:inputcheckbox value="{!a.IsSelected}"></apex:inputcheckbox>
                            </apex:column>
                            <apex:column>
                                <apex:facet name="header">Account Name</apex:facet> 
                                <apex:outputlink value="/{!a.AccountId}">{!a.AccountName}</apex:outputlink>
                            </apex:column>
                            <apex:column value="{!a.AccountNumber}" headervalue="Account Number"></apex:column>
                            <apex:column value="{!a.Industry}" headervalue="Industry"></apex:column>
                            <apex:column value="{!a.Website}" headervalue="Website"></apex:column>
                        </apex:pageblocktable>
                         <apex:outputpanel layout="block" styleclass="listViewport">


<div class="bottomNav">


<div class="paginator">
                                        <apex:panelgrid id="gridPaging" columns="3" width="100%" columnclasses="left, center, right">
                                            <apex:panelgroup>
                                                <span class="selectorTarget">
                                                    <strong>{!LNumber}-{!UNumber} of {!RecordCount}</strong>
                                                </span>
                                                <span>  </span>
                                                <span>
                                                    <strong>Total {!RecordCount} records</strong>
                                                </span>
                                                <span>  </span>
                                                <apex:actionstatus id="statusPaging">
                                                    <apex:facet name="start">
                                                        <img src="/img/loading.gif" height="14px" width="14px">
                                                    </apex:facet>
                                                    <apex:facet name="stop">
                                                        <img src="/img/s.gif" height="14px" width="14px">
                                                    </apex:facet>
                                                </apex:actionstatus>                                           
                                            </apex:panelgroup>
                                            <apex:panelgroup>
                                                <span class="prevNextLinks">
                                                    <span class="prevNext">
                                                        <apex:commandlink id="linkMoveFirst" status="statusPaging" action="{!First}" rerender="gridPaging, pbAccounts" rendered="{!allowMovePrev}">
                                                            <img src="/s.gif" title="First" alt="First" class="first">
                                                        </apex:commandlink>
                                     
                                                        <apex:outputpanel layout="none" rendered="{!NOT(allowMovePrev)}">
                                                            <apex:image url="/s.gif" title="First" alt="First" styleclass="firstoff"></apex:image>
                                                        </apex:outputpanel>
                                                    </span>
                                                     
                                                    <span class="prevNext">
                                                        <apex:commandlink id="linkMovePrev" title="Previous" status="statusPaging" action="{!Prev}" rerender="gridPaging, pbAccounts" rendered="{!allowMovePrev}">
                                                            <img src="/s.gif" title="Previous" alt="Previous" class="prev">
                                                            <span>Previous</span>
                                                        </apex:commandlink>
                                                        <apex:outputpanel layout="none" rendered="{!NOT(allowMovePrev)}">
                                                            <apex:image url="/s.gif" title="Previous" alt="Previous" styleclass="prevoff"></apex:image>
                                                            <span>Previous</span>
                                                        </apex:outputpanel>
                                                    </span>
                             
                                                    <span class="prevNext">
                                                        <apex:commandlink id="linkMoveNext" title="Next" status="statusPaging" action="{!Next}" rerender="gridPaging, pbAccounts" rendered="{!allowMoveNext}">
                                                            <span>Next</span>
                                                            <img src="/s.gif" title="Next" alt="Next" class="next">
                                                        </apex:commandlink>
                             
                                                        <apex:outputpanel layout="none" rendered="{!NOT(allowMoveNext)}">
                                                            <span>Next</span>
                                                            <apex:image url="/s.gif" title="Next" alt="Next" styleclass="nextoff"></apex:image>
                                                        </apex:outputpanel>
                                                    </span>
                             
                                                    <span class="prevNext">
                                                        <apex:commandlink id="linkMoveLast" status="statusPaging" action="{!Last}" rerender="gridPaging, pbAccounts" rendered="{!allowMoveNext}">
                                                            <img src="/s.gif" title="Last" alt="Last" class="last">
                                                        </apex:commandlink>
                             
                                                        <apex:outputpanel layout="none" rendered="{!NOT(allowMoveNext)}">
                                                            <apex:image url="/s.gif" title="Last" alt="Last" styleclass="lastoff"></apex:image>
                                                        </apex:outputpanel>
                                                    </span>
                                                </span>
                                            </apex:panelgroup>
                                            <apex:panelgroup>
                                                <span class="selectorTarget">
                                                    <strong> Page {!PageNumber} of {!PageCount}</strong>
                                                </span>
                                            </apex:panelgroup>
                                        </apex:panelgrid> 
                                     </div>


                                </div>


                            </apex:outputpanel>
                    </apex:outputpanel>
                </apex:facet>
            </apex:pageblocksection>
        </apex:pageblock>
    </apex:form>
     


<style type="text/css">
        .custPopup{
            background-color: white;
            border-width: 2px;
            border-style: solid;
            z-index: 9999;
            left: 50%;
            padding:10px;
            position: absolute;         
            width: 700px;
            margin-left: -320px;
            top:100px;
        }
        .popupBackground{
            background-color:black;
            opacity: 0.20;
            filter: alpha(opacity = 20);
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            z-index: 9998;
        }
 
    </style>


</apex:page>

Output:

1

 

2

 

3

 

4