I have a commandbutton, When click I want it to save a phonecall record and redirect to the record page, after that open a new page in a new window which shows the editing page of a new case related to the call.
I am doing it like this:
//Pseudocode
public PageReference saverecord(){
create a new phone call record;
insert record;
if(createnewcase)
create case;
insert case;
create editcaseURL;
return phonerecord page;
}
on the client side, In the commandbutton, I use <apex:param name="newcase" value="true" assignTo="{!createNewCase}"/> to set the createnewcase to true. and oncomplete javascript to open the popup window. I have test the phonerecord and caserecord seperately and succeeded. but when I put them in one class, the case was never created. and in the visualforce view state window. I can't even find the boolean createnewcase.
please help.
Thanks
It is easy to implement with Apex / JavaScript:
The apex code:
// Defining two variables for the both records (case and phone)
public String caseId { get; set; }
public String phoneId { get; set; }
// Defining a boolean to render the JavaScript panel after saving
punlic Boolean redirectNow { get; set; }
public PageReference saverecord(){
// Create a new phone call record;
insert record;
// Now reading a phone record id
phoneId = record.id;
if(createnewcase){
create case;
insert case;
// Now reading a case id
caseId = case.id;
}
// If case and phone are OK -> set this flag to true
redirectNow = true;
// We do not need to return a real page reference
// because we will redirecting with javascript
return null;
}
And the Visualforce page with JavaScript:
<!-- A command button, that rerenders a panel with JavaScript -->
<apex:commandButton value="Save" action="{!saverecord}" reRender="doAfterSave"/>
<!-- This panel renders only after you will save the new records -->
<apex:outputPanel id="doAfterSave">
<apex:outputPanel rendered="{!redirectNow}">
<script>
// First reading the id's
var newCaseId = '{!caseId}';
var newPhoneId = '{!phoneId}';
if(newCaseId != '' && newPhoneId != '')
{
// Now opening a new window with case in edit mode
window.open('/' + newCaseId + '/e');
// And then redirecting the main page to the new phone record
window.location = '/' + phoneId;
}
</script>
</apex:outputPanel>
</apex:outputPanel>
If you want to open newly created case in edit mode then use following code.
public PageReference saverecord(){
create a new phone call record;
insert record;
if(createnewcase)
{
create case;
insert case;
return new PageReference('/' + case.Id + '/e');
}
else
do other logic here
return null;
Although this article is dated, if you really need to use JavaScript and popups to get this job done (as opposed to simply using Visualforce pages and passing parameters in the url, or using VF pages that share the same controller thereby maintaining the same context, member variable values, etc.), I'd take a look at this tutorial: Tutorial: Modal Dialogs in Visualforce using the Yahoo! User Interface Library
The essential trick is to make the "popup" really just a hidden aspect of the same page to circumvent issues associated with passing around values with JavaScript in Visualforce.
From the tutorial:
Building a link or button to popup a new Visualforce page is actually
quite simple, and getting this popup to be above the current page is
also quite easy. The problem occurs when you would like to collect
information in the popup and pass that information back to the page
that launched the popup.
The issue is that the new window is launched as a separate browser
request when you use window.open(). Since this is a separate request
to the server, the new page does not share the same controller
context/session. Even if the two pages both use the name of the same
controller! This is due to the fact that these are two different
requests at the browser level.
Related
I'm fairly new to MVC, and I seem to be having a problem returning a view from JS. I'm working on a chatting site, and this code creates a new chatroom with the name you assign it. Here's my JS code:
function CreateConversation() {
var conversationName = document.getElementById("conversationName").value;
var username = document.getElementById("usernameLabel").textContent;
window.location.href = "/CreateNewChat/CreateConversation?username=" + username + "&convName=" + conversationName;
}
That should call this ActionResult method:
public IActionResult CreateConversation(string username, string convName)
{
ChatModel model = new ChatModel(username, convName);
model.Chatters.Add(username);
return RedirectToAction("Chat","Chat", model);
}
Which does, as long as I don't type any conversation name in the text box. If I check the JS code, both username and conversation name values are correctly set up, but the ActionResult method will not be hit if I fill in the conversation name.
Here's the Chat method in the ChatController:
public IActionResult Chat(ChatModel model)
{
return View(model);
}
If I don't put any conversation name in, it will come all the way to this method, but it won't work (as that field is obligatory and I need it to load the view). Any ideas? I've tried using ajax but while it will pass the conversation name along, it will get stuck at the RedirectToAction statement (will never hit the Chat method in the ChatController). I'm really lost here.
Generally, you shouldn't pass objects around like that - at best you should only pass around ViewModels to prevent someone from fudging data and potentially breaking your app.
The way you have it with your redirect will actually push the whole model into the URL parameters before stepping back into code, leaving it wide open for someone to change as they see fit.
Ideally you would post to create a persisted instance then redirect by passing only an identifier to the Chat() method like so:
CreateNewChatController.cs
public ActionResult CreateConversation(string username, string convname)
{
Guid chatId = yourServiceFactory.CreateNewChat(username, convname);
// You passed the service the username anyway so it can do the Chatters.Add() internally
return RedirectToAction("Chat", "Chat", new { id = chatId });
}
ChatController.cs
public ActionResult Chat(Guid id)
{
ChatModel model = yourServiceFactory.GetChatById(id);
return View(model);
}
However, if you really want to pass the model onto ActionResult Chat(ChatModel model), then you can execute and return the controller action directly.
public IActionResult CreateConversation(string username, string convname)
{
ChatModel model = new ChatModel(username, convname);
model.Chatters.Add(username);
ChatController chatController = new ChatController();
return chatController.Chat(model);
}
I've created a very simplistic working example of this based on what you've said above, so any validation you may have in your ChatModel constructor is omitted in my example.
For any other errors, I would suggest checking to see if the values you're passing in aren't being detected as "potentially harmful" by the app and being dropped.
Noting that the use of .textContent instead of value would get the full contents between the start and ending tags of a given element, I'm assuming the element is a textarea type not a normal text input. You could be getting extra elements between <textarea>...</textarea>
In my example, my javascript function just takes two local variables declared and set within the function. Without seeing your submit page, I can't really comment on why else setting a value would prevent a call from working.
I'm facing a problem on the Save event of the Quick Create form for the Case entity. On Save event, I'm not able to get the record GUID in JavaScript code.
Our requirement is when the user clicks on Save button on Quick Create form of the Case, we would like to redirect the user to the newly created case record.
We have attached below Javascript function on save event of Quick Create form. This code works well on one of the 30-days trial instance, but it doesn't work well on the client development CRM instance.
setTimeout(function () {
var formContext = executionContext.getFormContext();
var caseId = formContext.data.entity.getId();
caseId = caseId.replace("{", "").replace("}", "");
var entityFormOptions = {};
entityFormOptions["entityName"] = "incident";
entityFormOptions["entityId"] = caseId;
Xrm.Navigation.openForm(entityFormOptions).then(
function (success) {
console.log(success);
},
function (error) {
console.log(error);
});
}, 1000);
Quick create form purpose is flyout data entry without going away from current form/record. The same QC form is being opened from different places like Dashboard, Subgrid, Main navigation too bar, Lookup, etc. and allow user to continue the ongoing stuff. Out of all these places - only from the Main navigation, after the save - system will prompt with a toast to navigate to that record or option to create another record. This is by design.
If you want to navigate to that record irrespective of the origin, you can try some scripts like yours but you are going to break the OOB pipeline flow. You may do some unsupported but it’s not recommended. I never tried it but you can add a function from onSave event, using eventArgs.preventDefault we can stop the OOB save, then open the Full Main form by prepopulating values from QC to Main form, then save it on form load.
Instead of all this pain, why not you open the Main form itself. :)
I am trying to build a console application using c# .net.. I need one particular functionality which is to retrieve values from an external website. The thing is we need to sign in to that website. I am able to open the page that i need using process.start in chrome signed in with the values i need.. but problem is when retrieving the value from page.. i thought of getting source code but every way i try it does not take the session and hence i am getting just error page source code as i am just entering the URL n not accessing the already opened tab? Is there any other way available either using JavaScript or c#?
Use WebClient API to login and download page data. You will need to make it cookie aware in order to maintain session. You will need to add reference to System.Web Assembly to use this API in console application.
public class CookieAwareWebClient : WebClient
{
public CookieAwareWebClient()
{
CookieContainer = new CookieContainer();
}
public CookieContainer CookieContainer { get; private set; }
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = CookieContainer;
return request;
}
}
Now use it like
using (var client = new CookieAwareWebClient())
{
var values = new NameValueCollection
{
{ "username", "john" },
{ "password", "secret" },
};
client.UploadValues("http://domain.loc/logon.aspx", values);
// If the previous call succeeded we now have a valid authentication cookie
// so we could download the protected page
string result = client.DownloadString("http://domain.loc/testpage.aspx");
}
I borrowed this piece of code from WebClient accessing page with credentials
Lots of time spent with other language/templating-engine combinations, however fairly new to JSF and have the "pleasure" of taking over a legacy JSF 1.1 project that uses a basic Sun JSF lib and for political reasons can not be updated to something more modern like JSF 2.2 with PrimeFaces or IceFaces, which I'd personally like to do after reading up on the capabilities of those.
The setup is a simple web app with landing page plus two tabs (Person & Address), each powered by their own Backing Beans and JSF pages. Six total navigation states as each tab starts with a "Search Form" page which leads to a "Search Results" page on submit and then a "View" page onmousedown (click action) of an individual result; for each of Person & Address data. The "Search Form" pages are session-scoped, and designed to pass search parameters around to each other in a header box entitled "User's Search Query". It does this because the "Search Results" page for Address searches is really an overloaded Person search result with Address thrown in for "Search for People by Address". Yes this could all probably be accomplished from a single page with some optional search params, but its not, and for whatever reason I'm not permitted to change the core page structure, just tasked to bug fix.
There's a long-standing bug I've been asked to fix, where one page (Person) always shows search parameters from the other (Address) when both are set, because of some incomplete logic (inside the panelGrid where the search query parameters are displayed):
<h:outputText value="Coverage Type:"/>
<h:outputText value="#{addressSearchUI.searchParameters.coverageType}"
rendered="#{empty personSearchUI.searchParameters.coverageType}"/>
<h:outputText value="#{personSearchUI.searchParameters.coverageType}"
rendered="#{!empty personSearchUI.searchParameters.coverageType}"/>
A little too overly simplistic; if a Person search's Coverage Type was set, use that, but if not, try to use the Address coverageType search parameter. Works great when only one or the other were set.
PROBLEM CASES:
Since they are session scoped, what if both of them were set in separate searches and we're on the Address page? The value from the old Person search displays (and vice versa; this is the main problem). What if neither of them are set yet? It should show a default value of ALL COVERAGE, easy enough to add but doesn't address that main problem.
SOLUTION:
Thought this would be an easy fix but I've hit a major wall trying to do either one of the following two simple things that I think would solve my problem:
A) get the session-scoped value of one page's search parameter and reset it or set to empty string (then that naiive presentation logic could be left as is)
B) call the reset button of one page programmatically from Java when backing bean init() method gets called as we switch tabs (I can call the method but am not sure how to get the original session instance rather than creating a new one, which will fire to a non-existent page UI-wise)
Here's what I've tried so far:
A) Adding an actionListener attribute to the submit button itself and trying to forcibly reset values from the other Search Form.
<h:commandButton
id="personReset"
styleClass="submitButton"
value="Reset"
action="#{transactionSearchUI.resetSearch}"/>
<h:commandButton
id="personSearch"
styleClass="searchButton"
value="Search"
rendered="#{personSearchUI.canDo['performSearch']}"
actionListener="#{addressSearchUI.resetSearch}"
action="#{personSearchUI.performSearch}"/>
B) In the backing bean of each search, try to invoke the reset button of the other:
/* utility method */
private UIComponent findComponent(String id, UIComponent where) {
if (where == null) {
return null;
}
else if (where.getId().equals(id)) {
return where;
}
else {
List<UIComponent> childrenList = where.getChildren();
if (childrenList == null || childrenList.isEmpty()) {
return null;
}
for (UIComponent child : childrenList) {
UIComponent result = null;
result = findComponent(id, child);
if(result != null) {
return result;
}
}
return null;
}
}
//... then inside init() method that is called when Person tab loads
FacesContext facesContext = FacesContext.getCurrentInstance();
UIViewRoot root = facesContext.getViewRoot();
HtmlCommandButton button = (HtmlCommandButton) findComponent("addressReset", root);
ActionEvent actionEvent = new ActionEvent(button);
actionEvent.queue();
Fails with NullPointerException, so clearly can't access one tab from the other's backing bean in this way (unless I'm missing something).
C) JavaScript approach to reset values from one tab to the other:
<script type="text/javascript">
function clearAddressSearchParams() {
document.getElementById('AddressSearch:addressSearch:addressReset').click();
document.forms['AddressSearch:addressSearch'].reset();
return false;
}
window.onload = clearStats;
</script>
Neither of those client-side methods of trying to fire the "Reset" button action are going to work, since again, the "Person" and "Address" Search Forms were setup as two totally separate beans and pages (even though one ultimately extends the other), one won't be active (or available in the DOM) so it won't find the elements.
At this point, I know there is clearly a simpler solution, but I'm at my wits end and probably coming at this from the wrong angle, so I don't know what else to do but put up a bounty for some of the JSF geniuses here. But if anyone can suggest a fix (given my limitations of older JSF technology, no adding external libs and maintaining same basic code structure), you'll get the bounty and tons of appreciation.
EDIT: To summarize all this into a single question, can anyone suggest a reliable (legacy-JSF 1.1 compatible) way to flush a session variable or reset it, independently of its page where it gets set being active?
Good day!
I'm dealing with an issue for some time now and can't seem to find a solution. I have an usual ASP.NET MVC project on 'top' of which I added an Angular JS project. I also have a Web API, but this is not relevant for my issue. The web application itself is the Angular project, making calls to the API.
I used the default authentication system from ASP.NET MVC using the default Login.cshtml View and default AcountController methods for login/logout.
The problem I'm dealing with is the following:
A user enters the website and is prompted with the login form. After inserting valid account details, he is redirected to the main page (index, from the angular js project). IF the user clicks on the Back button of the browser, he is prompted with login form again and if he inserts his user and password again (or any other sign-in details), I receive a HttpAntiForgeryException with the following message: "The provided anti-forgery token was meant for a different claims-based user than the current user."
I tried disabling the back button with javascript (window.history.forward(1);), but it doesn't work apparently on older browser versions and it's by far an elegant solution. I tried reloading the login page (because after clicking back, if you reload the page you will be redirected to the index page (since the session is still valid)) and none of these solutions really work.
Any ideas?
Thank you in advance!
Update: so far I've included AntiForgeryConfig.SuppressIdentityHeuristicChecks = true; in Application_Start() and also this:
public class HandleAntiForgeryError : ActionFilterAttribute, IExceptionFilter {
#region IExceptionFilter Members
public void OnException(ExceptionContext filterContext)
{
var exception = filterContext.Exception as HttpAntiForgeryException;
if (exception != null)
{
var routeValues = new RouteValueDictionary();
routeValues["controller"] = "Account";
routeValues["action"] = "Login";
filterContext.Result = new RedirectToRouteResult(routeValues);
filterContext.ExceptionHandled = true;
}
}
#endregion }
[HandleAntiForgeryError]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
}
The only issue left is when I click back and try to login with another user it fails. Meaning I remain logged in with the previous user. I expect this to be normal, but is there a way to change that (as in when I click back and enter other user's credentials, to get logged in with those new credentials (even though I'm already logged in)).
SOLVED: In my code I had the following line:
if (User.Identity.IsAuthenticated)
return RedirectToAction("Index", "App");
That is why after logging in with another user I was being redirected to index being logged in with the old credentials.
I had this same issue and i solved this issue by adding following line in Application_Start() event in Global.asax in my case:
AntiForgeryConfig.SuppressIdentityHeuristicChecks = true;
Also add this in Application_Error():
Exception ex = Server.GetLastError();
if (ex is HttpAntiForgeryException)
{
Response.Clear();
Server.ClearError(); //make sure you log the exception first
Response.Redirect("~/Home/Index", true);
}