+ Start a Discussion
AnnieGAnnieG 

Can you capture the "Save" action and preform custom actions?

So we can replace and redevice the logic for he standard buttons on the detail pages such as delete,
I tried, but seems to me there is no go for the Save button. Am I right?
Seems that only applies to the standard buttons, which doesn't include "Save".  So what can I do if
I want some extra logic embeded in the "Save" action? Like when the "Save" button on salesforce is pressed, I also want the record to be created or updated on our company's side.

Thanks for your input.
tocktock

This is exactly the problem I have, see here: http://community.salesforce.com/sforce/board/message?board.id=general_development&message.id=10624 

the outbound message solution i was given would work, but it appears to be limited to data from 1 Sobject. i needed to post the data from 2 Sobjects.

morleymorley

We faced a similar problem. We looked at outbound messaging, but we needed our onSave actions to happen synchronously, so that didn't really work for us. Instead, we used a neat little trick which combines ideas from various helpful posters on this forum:

  • write a custom s-control that directs the user to the standard edit page of the object in question, but sets the saveURL to point to your custom component that will perform the action. The saveURL needs to include the ID of the object.
  • override the Edit button for the object in question with your s-control.
  • when the user clicks Edit, they get the standard edit page, then when they click Save, they're redirected to your component, which performs whatever actions you need.

In our example, the user edits our custom Order object and saves it with the custom checkbox "approved" set to true. Our onSave component checks for that value and then generates an Invoice object and related Invoice Items.

Hope that helps. I can send you the s-control code if you like.

 

Glenn.

gsickalgsickal
Your solution sounds very creative.  Would you mind posting your code or a portion of it so we can see how you did it?
morleymorley

The s-control code is of type HTML. Here's the code:

Code:

<html> 
<head> 
<script src="/soap/ajax/9.0/connection.js"> 
</script> 
<script> 
function init() 
{ 
// returns the standard Transaction Edit page, but sets the saveURL to point to our component so we can capture the onSave event, and sets the returnURL to whatever it was set to by the standard Salesforce edit button (i.e. Transaction or Opp, depending on where the user clicked edit. GE 15/02/07 

window.parent.location.href = "{!URLFOR($Action.Transaction__c.Edit , Transaction__c.Id , [saveURL=URLFOR("http://www.oursite.com/app/TransactionSaveController.htm", Transaction__c.Id ), 
retURL=URLFOR($Request.retURL, null,null,true)] ,true)}" 

} 
</script> 
</head> 
<body onload="init()"> 
<p>&nbsp;</p> 
</body> 
</html>


 
This is triggered by overriding the Edit button for our custom object Transaction__c. The s-control simply redirects the user to the standard edit page, but defines the saveURL parameter to point to our own Java component (which we host on our server), passing in the TransactionID. Our component then uses this ID to look up related SF objects and create new ones based on the value of several fields.

Note that the saveURL is really an aftersaveURL. The standard SF save occurs, then we get control, in that order. Note also that the Edit button override works everywhere in SF except the mini-view in the console (I've had to disable that for our users).

This works a treat for us. Hope you have success too.

 

Glenn.

gsickalgsickal
Very nice and simple.  I am surprised Salesforce doesn't have an override for Save or Cancel since they do for most of the other standard buttons like New, Edit, Clone, etc.
SteveBowerSteveBower

I haven't tried this, but instead of redirecting it towards your external web page, could you redirect it to an S-Control using a relative link, or the frontdoor mechanism?  You can still pass all the parameters, etc.  There's no compelling reason to go outside Salesforce unless you need to anyway. I think.

I think that allowing a "save" override might be a little more difficult than some of the others because if the "intermediate" state of the values.  You've edited them, but they haven't been pushed to the actual object yet.
Or, what if your intermediate code fails for some reason?  Did the save get ignored?  That shouldn't really be "allowed". I'm think with Apex a lot of this will be cleaned up.   :-)

Thanks, Steve.

marie.tournemarie.tourne
Thanks for this code, it is really handy, I can imagine it was hard to figure out this hack.
I have tested it :
First, I if the intermate code fails, the save action occurs anyway.
Second, yes you can use an SControl for the redirection (using URLFOR($SControl.<s-control name>, ...)

So it is technically possible to override Save action (!!!)
tocktock

That's a great solution morley.

cheers

SteveBowerSteveBower
This *is* a sweet, albeit somewhat strange, solution to the PostSave situation. 

However, If the user executes "Save & New" instead of "Save" from the Edit screen, the SControl is bypassed and we go directly to the New screen. 

Anybody have any thoughts about how to avoid that situation?

Thanks, Steve.

morleymorley

Hmmm ... that's a good point (which I'd missed until now!). I've had a look and it seems there is another parameter called "save_new_url", which presumably we can use in the same way as SaveURL. Has anyone tried this? I'll give it a go and repost.

 

Morley.

morleymorley

OK, I've looked into this and I think I've solved it. We need to specify the "save_new_url" and hard code a "newid" for it, because SF doesn't automatically pass newid to save_new_url like it does for saveURL. I've only done some quick testing, but the code here seems to do the trick.

 

Code:

<html> 
<head> 
<script src="/soap/ajax/9.0/connection.js"> 
</script> 
<script> 
function init() 
{ 
// returns the standard Transaction Edit page, but sets the saveURL to point to our component so we can capture the onSave event, and sets the returnURL to whatever it was set to by the standard Salesforce edit button (i.e. Transaction or Opp, depending on where the user clicked edit. GE 15/02/07 

// GE 16/04/07: amended to handle Save and New. 

window.parent.location.href = "{!URLFOR($Action.Transaction__c.Edit , Transaction__c.Id , [saveURL=URLFOR("http://www.oursite.com/TransactionSaveController.htm", Transaction__c.Id ), 
save_new_url=URLFOR("http://www.oursite.com/TransactionSaveController.htm", Transaction__c.Id, [newid=Transaction__c.Id] ), 
retURL=URLFOR($Request.retURL, null,null,true)] 
,true)}" 

} 
</script> 
</head> 
<body onload="init()"> 
<p>&nbsp;</p> 
</body> 
</html>


 
Thanks for everyone's input. I'd be glad to hear any further suggestions on how to improve this.

 

Thanks,

Morley.

SteveBowerSteveBower
Hmmm... I believe this is only working for you because your own script TransactionSaveController.htm is reading
it's parameters and changing behavior as needed if a newid parameter is passed?  This is great, however I
wanted three things that were slightly different:

First, I wanted to only send *one* parameter to my s-control to signify where to return in all cases. 

Second, I wanted to do this with S-Controls instead of a remote web service, and

Third (which you might test on yours), is after doing a "Save & New", the user is brought to the Recordtype
Picklist page.  If, at that point they hit Cancel, I wanted to bring them back to the original object's page in the
View mode after the save is completed.  I think yours will bring them back to the object in Edit mode.

So, I've done the following.

1. Create a snippet which is the Actual Work I want to do.  In this case I'm rolling up T & E expenses for a heirarchy
of Accounts.  I put that in a Snippet called Account_Rollup_Helpers.  The entry point into that is "rollupTandE(id)".

2. I created a snippet which has code to let me get to my URL parameters easily.  I'll include this below.
I put it in a snippet called Querystring.  (This is useful in general)

3. I created an intermediate S-Control which calls the code in the snippets above.  I could have put it all in the same
s-control, but this was cleaner.  This is the Rollup_T_E S-Control which gets the Action Parameter
from the URL, calls the Rollup code to do the work, and then refers to a new page using the Action parameter.

4. I created an AccountPostSave S-Control.  Based on yours, it passes a single "Action" parameter which is where
my S-Control gives control when it's done.  This one Action parameter has both a default destination and a
cancelURL destination so that the cancel operation goes back to the right place.

So, this sounds far more complicated that it is.  (But the nesting is moderately hellacious.)


In reverse order:

4. Code:
<html> 
<head> 
<script src="/soap/ajax/9.0/connection.js"></script> 
<script> 
function init() 
{ 
// Returns the standard Account Edit page, but sets the saveURL to point to our S-Control 
// so we can capture the onSave event, and sets the returnURL to whatever it was set to 
// by the standard Salesforce edit button (i.e. Account).
// Thanks: Credit to GE 15/02/07 (Glenn - Morley) On Discussion boards.
//
// Also sets the save_new_url to take care of the save & new situation.
// Along with both saveUrl and save_new_url passes an Action parameter
// which the s-control should use for it's return destination if 
// standard behavior is desired.
// sbower. 4/15

window.parent.location.href = 
 "{!URLFOR($Action.Account.Edit , Account.Id , [saveURL=URLFOR($SControl.Rollup_T_E, Account.Id, [Action=URLFOR($Action.Account.View, Account.Id)] ), retURL=URLFOR($Request.retURL, null,null,true), save_new_url=URLFOR($SControl.Rollup_T_E, Account.Id, [Action=URLFOR($Action.Account.New, Account.Id,[cancelURL=URLFOR($Request.retURL, null,null,true)])], false )] ,true)}"; 

}
</script> 
</head> 
<body onload='init()'> 
</body> 
</html>

3. The intermediate S-Control.  Includes the other two via Include directive at the top:

 
Code:
<html>
<head>
<script src="/soap/ajax/9.0/connection.js"></script>
{!INCLUDE($SControl.Account_Rollup_Helpers)}
{!INCLUDE($SControl.Querystring)}
<script type="text/javascript">
function init() {
 var x = new Querystring();
 rollupTandE("{!Account.Id}");
 window.parent.location.href = x.get("Action");
}
</script>
</head>
<body onLoad='init()'>
</body>
</html>

2. The Querystring Snippet.

Code:
<script type="text/javascript">
/* Client-side access to querystring name=value pairs
 Version 1.2.3
 22 Jun 2005
 Adam Vandenberg
*/
function Querystring(qs) { // optionally pass a querystring to parse
 this.params = new Object();
 this.get = Querystring_get;
 //alert("location.search = " + location.search);  Debug
 if (qs == null)
  qs=location.search.substring(1,location.search.length);

 if (qs.length == 0) return;
// Turn <plus> back to <space>
// See: http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4.1
 qs = qs.replace(/\+/g, ' ');
 var parameters = qs.split('&'); // parse out name/value pairs separated via &
// split out each name=value pair
 for (var i=0;i < parameters.length;i++) {
  var value;
  var pair = parameters[i].split('=');
  var name = unescape(pair[0]);
  if (pair.length == 2)
   value = unescape(pair[1]);
  else
   value = name;
  this.params[name] = value;
 }
}
function Querystring_get (key, default_) {
 // This silly looking line changes UNDEFINED to NULL
 if (default_ == null) default_ = null;
 var value=this.params[key];
 if (value==null) value=default_;
 return value;
}
</script>

 1. My S-Control which does all the work... (Not relevent to this discussion so I'm only showing the entry point.)

 
Code:
function rollupTandE(id) {

// Does it's work and then returns.

}

 

SteveBowerSteveBower
The only problem with everything I just posted is... it doesn't work.

So, forget all that for now.  Any ideas out there?  It navigates as expected... the only issue is, and it's a big one,
the new values aren't actually saved in the record.

Oh Joy.  Steve.

Clearly this whole "working around Salesforce" stuff is tiring.  Makes we want to just add an Event handler to the Save button with the old "Pack evil code into section headers" hack.

morleymorley
That's a shame. That looked like a neat solution.
 
Indeed, finding ways around Salesforce's shortcomings is seriously tiring and almost a full-time job. I often get frustrated by it, but then I remind myself of just how much development work my team hasn't had to do(e.g. myriad complex UIs with easy customisation, detailed security and audit trail, reporting, etc.) and I'm happy I made the right decision to buy into the Salesforce vision. For me, if we could easily capture onSave properly and if we could just get objects to refer to each other properly (like in an old fashioned SQL query), then I'd be happy. For now I rely on my fellow hacker community!
 
 
Morley.
Chirag MehtaChirag Mehta

Goody one ,but can u hlp me in following

This thread discusses abt capturing save on a EDIT button.I want to capture save on a NEW Button.

I had posted the complete soltuion @

http://community.salesforce.com/sforce/board/message?board.id=general_development&message.id=11319

 

The onlyproblem in above solution is that the SAVE_NEW_URL doesnt works, tried all possible ways would like to hear from you if i m missing anything.

Rest all things works fine




Message Edited by Chirag Mehta on 05-16-2007 04:07 AM

richanshrestharichanshrestha
Wow, I was also having the same problem. But in addition, i got to implement that for console view as well.
Can we implement saveURL logic in Console?
 
Thanks,
Richan
 
 
morleymorley

Partially. When you override the Edit button, it works in the main detail pane of the console (the bottom left pave), but it doesn't override the Edit button in the related object mini page layouts that appear on the right. At my organisation we've disabled editing on mini page layouts for that reason.

Note that as of the Summer 07 release, if you have Unlimited Edition, you can now do this all much more neatly with triggers in Apex. Apex may well be productionised soon for Enterprise Edition as well.

 

 

Morley.