Here's an idea for an overall simpler solution. First, you need a way to expose your information to a 3rd Party (your "Users" without access). This is exactly what Site
s are for. Now, creating the pages & code to properly verify users and log them in as well as creating methods to guarantee that you don't expose data you don't want to (sessionCheck
methods) are not simple tasks, but they're not too complex either. And isn't too difficult to generalize the concept so it can be reused for ANY Site you need User-Authentication for.
Anyways, here's the main course: To view ANY record with it's standard layout, try the apex:detail
tag. And if you need the page rendered as a PDF, use the renderAs="pdf" attribute in the apex:page
tag.
So, the case VF page would be as simple as:
<apex:page standardController="Case" extensions="sessionCheckController" renderAs="pdf" action="{!sessionCheck}">
<apex:detail subject="{!Contact.id}"/>
</apex:page>
With controllerExtension:
public class sessionCheckController{
private final id recordId;
public sessionCheckController(ApexPages.StandardController con){
this.recordId=con.getId();
// ...
}//END init(ApexPages.StandardController con)
public pageReference sessionCheck(){
if(!MySite.isSessionValid()) // call the class that manages User-Authentication
return MySite.getLoginPage();
return null; // stay on the page if isSessionValid = true
}//END sessionCheck()
}//END sessionCheckController
So, the moral of the story is that once you build the User-Authentication Framework (Apex Class(es) + VF Pages [Login, HomePage, Logout, Register?, Forgot Password?, Error Pages, etc.]), it's EASY to expose any Salesforce data. And this is especially EASY if you just want to show a record's Standard Layout via apex:detail
.
Update
Check out this post
How to roll your own authentication for an external Salesforce App?
where I describe how to build the Authentication Interface I mentioned in my above solution. Also, notice there's a link in that post to a second post where I go into even more detail on how the whole structure:
- a Custom Object to hold User/Session/Site information,
- an Apex Class called
Portal
that houses all of the static methods like: login()
, logout()
, isValidSession()
, register()
, etc. Also, have static methods to document any action we take -- such as creating a "Session" record for a User documenting whatever we like.
- have
Portal
as an Instance act like an instance of ApexPages.StandardController()
where the Portal
Instance
has methods that grab the User + Session info from the SessionCookie
, check to see if the session is valid, if so, populate the instance so the instance methods like getUserId()
return actual data (because the session is verified. Note we really are mimicking what Salesforce does with ApexPages.StandardController
s. It's great for anybody trying to implement MVC.)
So, again, the idea is we have an sObject called "Portal" that tracks (at least) three types of things:
- Site settings - records of this type have a RecordType.Name = '
Site
'. Store parameters here that relate to the Site. Example: an integer called Session_Length__c to be used in the static method Portal.isSessionValid()
where a Session is "Valid" if CalculatedSessionTime < Session_Length__c
.
- User Details - records of this type have RecordType.Name = '
User
'. Typically, you'll have another object where each record of that object will need a "User" for the Portal. Example: for an 'Account' portal, Portal__c
User
records for the Account-portal. For the Employee-Portal, we'll have User
records lookup to the Employee__c
object.
- Session info - records of this type have RecordType.Name = '
Session
'. This will hold the SessionID and anything else relavent to one particular session. It will have a lookup to a Portal_User
record (Portal__c
record with RecordType.Name='User'
), and this lookup should act like a Master-Detail relationship.
That's the basic setup for the Object, and there is a lot of room for enhancement depending on your need. I prefer having one object with many RecordType
s, but this can eloquently be done with a Custom Object per RecordType.
The next part is the build the Apex Class, Portal
, to do all the work. First, have any general action be a static
method (like it should be). Examples are generic actions any Authentication Interface will have to do: createUser()
, register()
, isSessionValid()
, login()
, changePassword()
, logout()
, createNewSession()
, etc. (As you walk through Use Cases, it'll become evident which methods you need. A Portal-User will get to the Login Screen, then they'll need to login. Then when they get sent to the Home page, we'll need to verify the Session, . . . )
Then, the idea is to have an instance of Portal
act like an instance of ApexPages.StandardController
. So, the constructor will try to get the Username
and SessionId
out of the SessionCookie
, and do the SOQL queries to get the UserId
+ whatever else you want to have handy as instance methods
.
Another idea I've had for implementation is create an interface
for all Portal Controllers.
public interface PortalInterface{
pageReference sessionCheck();
}
How would I use this? Suppose we have the Visualforce Page Portal_HomePage
using Controller Con_Portal_HomePage
.
Portal_HomePage:
<apex:page controller="Con_Portal_HomePage" action="{!sessionCheck}">
Con_Portal_HomePage
public class Con_Portal_HomePage implements PortalInterface{
private Portal portalController;
private id userId;
public Con_Portal_HomePage(){
this.portalController=Portal.getNewPortalController();
}//END init()
private void customInit(){
// things to initialize if we have a valid session
this.userId=portalController.getUserId();
}//END customInit()
public pageReference sessionCheck(){
pageReference sessionCheckResult=portalController.getSessionCheck();
if(sessionCheckResult==null){
// then the session is valid / stay on the same page
this.customInit();
}
return sessionCheckResult;
}//END sessionCheck
Now, I could probably jazz this up by creating a Visualforce Template that calls the action method sessionCheck()
, but the point is, once logged in, you'll need to verify the session every time a user goes to a different page in your "portal" / Site
.
Again, check out the post in the link given above, and make sure to check out the link to the second post too. If you're still reading this, it's probably worth the look. Though this will take some work to initially build, the ideas behind the entire implementation are fairly simple, and the real work lies in mapping out all of the possible Use Cases. And once you have the framework built, it's highly reusable.
Maybe this idea will be packaged up when I get a chance to finely tune it.
Best Answer
I would parse the blob into a string and strip certain kinds of html tags for the following reasons :
I suggest you look at Best Practices for Rendering PDF's in the APEX documentation and much of my reasoning will become readily apparent. Creating PDF's from known input data can be tricky enough as it is. Rendering them from other sources which you have no control over would be totally hit or miss without first stripping extraneous tags or links to embedded external content.