Monday, January 31, 2011

Access Salesforce Content on your VF Page


Recently I came across the Salesforce Content Related requirement, Salesforce only provide limited access to the content (ContentVersion object). It does not allow you to access the Content & show in your custom VF page.
Here Me & my friend Vinod did a lot of Rnd & found a easy solution. This post will save your lot of time if you are doing this kind of task with Content.

To implement its example, take a div
<div class="centerContent" id="myDiv"></div>
There are two simple JS function which will help to get salesforce content –

function OpenDoc(docId){
 
var htmlString = '<div id="chatterFileViewerPanel" class="chatterFileViewerPanel"><br/><br/> <embed height="500px" align="middle" width="500px" type="application/x-shockwave-flash" wmode="transparent" pluginspage="http://www.adobe.com/go/getflashplayer" allowfullscreen="true" allowscriptaccess="sameDomain" ' +
'name="renditionLarge" bgcolor="#f3f3f3" quality="high" id="renditionLarge" '
+'flashvars="shepherd_prefix=/sfc/servlet.shepherd&amp;v='+docId+'&amp;mode=chatterfilepreview&amp;in_tests=false" src="/static/101210/sfc/flex/DocViewer.swf" /> </div> ';
           
        document.getElementById('myDiv').innerHTML =  htmlString;
        PrepareFlexComponent(docId);
       
       
}

function PrepareFlexComponent(docId){
        insertFlexComponent('/static/102010/sfc/flex/DocViewer',
                'shepherd_prefix=/sfc/servlet.shepherd&v='+docId+'&mode=chatterfilepreview&
                amp;in_tests=false','500px', '500px', '#f3f3f3', 'chatterFileViewerPanel', 'renditionLarge', false,
                { adobeFlashVersionLbl : 'You must enable or download Adobe Flash Player version 9.0.115 or 
                   later to use this feature.',
                   downloadAdobeLbl : 'Download Adobe Flash Player', downloadAdobeLinkLbl : 'Click the 
                   link below to download the Adobe Flash Player:' closeDialogLbl : 'Cancel'});
                   
        Ninja.Flex.ContentStaticResourcesBase = '/static/102010/sfc';
        Ninja.Flex.ShepherdController = '/sfc/servlet.shepherd';
}  



Put below script as it is in your page


<script type="text/javascript" src="/static/101210/js/functions.js"></script>
<script  src="/EXT/ext-3.0.0/ext.js" type="text/javascript"></script>
<script  src="/jslibrary/1294273133000/main.js" type="text/javascript"></script>
<script  src="/jslibrary/1289945557000/Chatter.js" type="text/javascript"></script>
<script  src="/jslibrary/1289346548000/ChatterFiles.js" type="text/javascript"></script>
<script  src="/jslibrary/labels/1295420058000/en_US.js" type="text/javascript"></script>
<script type="text/javascript" src="/static/101210/desktop/desktopAjax.js"></script>
             
<script type="text/javascript" src="/static/101210/sfc/javascript/lib/AC_OETags.js"></script>
 
<script>

   function postSubDocument(url, internal) {
      var form = document.getElementById("formSubDocument");
      form.action = url;
      form.internal.value = internal;
      form.submit();
    }
             
    function hideFlex() {
        document.getElementById('IE6Confirm').style.display = "block";
    }
                    
    function showFlex() {
        document.getElementById('IE6Confirm').style.display = "none";
    }
                    
    function cancelDownload(url) {
        showFlex();
    }
                    
    function triggerDownload(url) {
        window.open(url);
    }
                    
    function downloadDocForIE6() {
        showFlex();
        triggerDownload(_url);
    }
                    
    function downloadDoc(url) {
        var isIE6 = (navigator.userAgent.indexOf("MSIE 6") != -1);
        if (isIE6) {
            _url = url;
            hideFlex();
        }
        else {
           triggerDownload(url);
        }
     }


  </script>

It’s done! Now call function OpenDoc by passing a ContentId & you can see your Content on VF page :)
It will show the content same as the salesforce standard way.
Enjoy..!!



Tuesday, January 25, 2011

Monday, January 24, 2011

Half Star Rating in jQuery Plug in

I recently used jQuery Star Rating in a site, in that I faced a issue regarding the half star rating, for Eg. If the rating is 2 than it can be shown easily but if it is 2.5 it is a little bit difficult to show the correct rating on the plug in. Here is the code if you are facing this type of issue-
Here is the VF page code & apex class code. In this the calculate method is most important to show the half star rating.
Apex Class
===============================================
public with sharing class StarRatingController {
public decimal selctedAverage{get;set;}
public decimal averageStar{get;set;}
public List getSkillOptionsStar() {
List skillOptions = new List();
decimal dOption= 1.0;
string val= '';
for(integer i=0; i<>
{
val = string.valueOf(dOption);
skillOptions.add(new Selectoption(val,val)) ;
dOption += 0.5;
}
return skillOptions;
}
public List getSkillOptions() {
List skillOptions = new List();
decimal dOption= 1.0;
string val= '';
for(integer i=0; i<>
{
val = string.valueOf(dOption);
skillOptions.add(new Selectoption(val,val)) ;
dOption += 0.1;
}
return skillOptions;
}
public void calculate(){
string str = string.valueOf(selctedAverage);
decimal tempAvg = 0;
if(str.indexOf('.') > -1)
{
str = str.substring(0, str.indexOf('.') + 2);
}
if(str.indexOf('.5') > -1)
{
tempAvg = Double.valueOf(str);
}
else
{
tempAvg = Double.valueOf(str);
tempAvg = tempAvg.round();
}
averageStar = tempAvg + 0.5;
}
public StarRatingController(){
selctedAverage = 1;
averageStar = 1.5;
}
}

VF Code
=============================================
<table cellspacing="5" cellpadding="0" border="0"><tr>
<td>Select average rating :</td>
<td>
<apex:selectList value="{!selctedAverage}" size="1" id="listSelect" onchange="calculate()">
<apex:selectOptions value="{!SkillOptions}"/>
</apex:selectList>
</td>
</tr>
<tr>
<td>Rating :</td>
<td valign="top">
<apex:outputPanel id="pnlResult">
<div id="divStarRating">
<apex:selectList value="{!averageStar}" size="1">
<apex:selectOptions value="{!SkillOptionsStar}"/>
</apex:selectList>
</div>
<script type="text/javascript">
$(document).ready(function(){
$("#divStarRating").stars({
inputType: "select",
split: 2,
cancelShow: false,
disabled : true,
starWidth: 17
});
});
</script>
<br/>
<br/>
<apex:outputLabel value="selected value==> {!selctedAverage}" /> <br/>
<br/>
<apex:outputLabel value="calculated value==> {!averageStar}" />
</apex:outputPanel>
</td>
</tr>
</table>
<apex:actionFunction name="calculate" action="{!calculate}" rerender="pnlResult" status="calStatus"/>
<apex:actionStatus startText="Please wait..." id="calStatus"/>


Sunday, January 23, 2011

Approval Process on VF Page

Take action approve/reject through custom VF page.
Generally to approve or reject an approval process standard way is used, but it’s quite interesting to access a record on custom VF page & approve/reject the record from this page. Here is the sample for this –
For this two objects are required one is master & other is detail. Let's say these two objects are professional object [Master] & review object [detail].
In the detail object there will be a field "Submitted_For__c" lookup (user) field. This field will be used for mapping to which user this review will be submitted.
Now coming to the code
===============================================
To access all review object for currently logged in user –
List listOfAllReviewObjects = new List([Select r.Proffesional_Info__c, r.Name, r.Id From Review__c r where r.User_SumittedFor__c = : UserInfo.getUserId() order by Id asc limit 1000 ]);
//iterate this list & create a set of listOfAllReviewObjects id’s. Let’s say name is “setOfAllReviewObjects”
now to access all approval process for currently logged in user
List listProcessInstance = new List([Select p.Id, p.Status, p.TargetObjectId, (Select ActorId, Comments, OriginalActorId, StepStatus From Steps order by CreatedDate asc) from ProcessInstance p where p.TargetObjectId in : setOfAllReviewObjects limit 1000]);
for(ProcessInstance pi:listProcessInstance){
objPi = new processInst();//here processInst is a custom class
objPi.apprProcId = pi.Id;
objPi.status = pi.Status;
if(pi.Steps.Size()>0)
{
//if object is approved/rejected, than Process Steps will have two entries
if(pi.Steps.Size()>1)
{ //these are the approved or pending records & size of steps will be greater than 1 in this case, initially there will be one entry in steps if no action taken on the record by approver
objPi.actorUserId = pi.Steps.get(1).ActorId;
objPi.reviewerName = mapReview.get(pi.TargetObjectId).Name;
objPi.reviwerId = pi.TargetObjectId;
objPi.originalActorUserId = pi.Steps.get(1).OriginalActorId;
objPi.comments = pi.Steps.get(1).Comments;
}
else
{ //all records which are submitted but pending to approve.
objPi.actorUserId = pi.Steps.get(0).ActorId;
objPi.reviewerName = mapReview.get(pi.TargetObjectId).Name;
objPi.reviwerId = pi.TargetObjectId;
objPi.originalActorUserId = pi.Steps.get(0).OriginalActorId;
objPi.comments = pi.Steps.get(0).Comments;

}

}
if(objPi.status == 'Approved' || objPi.status == 'Rejected'){
objPi.isApproved = true;
}
else {
objPi.isApproved = false;
}
to show the all records for current user, VF page code is as below-
<apex:pageBlockTable value="{!listProIns}" var="lpi" rendered="{!IF(listProIns.Size>0,true,false)}">
<apex:column headerValue="Approval Id" value="{!lpi.apprProcId}" />
<apex:column headerValue="Reviewer Name" value="{!lpi.reviewerName}" />
<apex:column headerValue="OriginalActorId" value="{!lpi.originalActorUserId}" rendered="false" />
<apex:column headerValue="Assigned Approver Name" value="{!lpi.originalActorUserName}" />
<apex:column headerValue="ActorId" value="{!lpi.actorUserId}" rendered="false" />
<apex:column headerValue="Actual Approver Name" value="{!lpi.actorUserName}" />
<apex:column headerValue="Comments">
<apex:inputTextArea id="txtComments" value="{!lpi.comments}" rendered="{!NOT(lpi.isApproved)}" cols="40" />
<apex:outputLabel value="{!lpi.comments}" rendered="{!lpi.isApproved}" />
</apex:column>>
<apex:column headerValue="Status" >
<apex:selectList value="{!lpi.Status}" multiselect="false" rendered="{!NOT(lpi.isApproved)}" size="1">
<apex:selectOption itemValue="NONE" itemLabel="--Action--" />
<apex:selectOption itemValue="Approve" itemLabel="Approve" />
<apex:selectOption itemValue="Reject" itemLabel="Reject" />
</apex:selectList>
<apex:outputlabel value="{!lpi.Status}" rendered="{!lpi.isApproved}"/>
</apex:column>
</apex:pageBlockTable>
<apex:pageBlockButtons id="blockButton" location="bottom">
<apex:CommandButton id="cmdButton" action="{!approvalProcessAction}" value="Save" rendered="{!IF(listProIns.Size>0,true,false)}"/>
</apex:pageBlockButtons>
On the VF page the records which are in pending state will show a drop down to take action while other records are not editable.
Finally to approve or reject a particular record, user will select a value from drop down & click on save button, the save button apex code will be-
Map mapStepIds = new Map();
List lstReview = new List([Select (Select ID,ProcessInstanceId from ProcessSteps where isPending=true) from Review__c where Id in :setPendingReviewObjects]);
if(lstReview.size()>0)
{
string id = '';
integer i = 0;
for(Review__c rv:lstReview)
{
if(rv.ProcessSteps.size() > 0)
{
id = rv.ProcessSteps[0].ID;
if(id != null && id != '')
{
mapStepIds.put(rv.ProcessSteps[0].ProcessInstanceId, id);
}
}
}

}
List listReq = new List();
for(processInst pi :listProIns)
{
if(!pi.isApproved && pi.status != 'NONE')
{
Approval.Processworkitemrequest objReq = new Approval.Processworkitemrequest();
objReq.setAction(pi.status);
objReq.setComments(pi.comments);
objReq.setWorkitemId(mapStepIds.get(pi.apprProcId));
listReq.add(objReq);

}
}
if(listReq.size()>0)
{
List res = Approval.process(listReq);
}
While submitting the action “setWorkitemId” is the most important part, this is the process instance id.

Enjoy!!!!