How to save a record and immediately use its GUID - dynamics-crm

I'm executing some javascript from a ribbon button and what I want to do is save the record that I am creating and then immediately use its GUID for some code a bit further on. Each time I try it the GUID is coming back null even though I'm requesting it after the record has been saved. If I try the button again after I've saved it then it works, but not as I'm saving it.
Is there a way to do this?
function RibbonButton_AddProduct()
{
//Save the Record
Xrm.Page.data.entity.save();
LoadProductCreate();
}
function LoadProductCreate()
{
var serverUrl;
var errorMessage = "Context to retrieve the Server URL is not available.";
if (typeof GetGlobalContext != "undefined"){
serverUrl = GetGlobalContext().getServerUrl();
} else {
if (typeof Xrm != "undefined"){
serverUrl = Xrm.Page.context.getServerUrl();
} else {
alert(errorMessage);
return;
}
}
if (serverUrl.match(/\/$/)){
serverUrl = serverUrl.substring(0, serverUrl.length - 1);
}
var recordId = Xrm.Page.data.entity.getId();
alert(recordId);
var url = serverUrl + "/main.aspx?etc=10030&extraqs=%3f_CreateFromId%3d%"+recordId
+"%257d%26_CreateFromType%3d10029%26etc%3d10030%26"
+"pagemode%3diframe%26preloadcache%3d1345465354543&pagetype=entityrecord";
window.open(url);
}

Here’s a different approach to solving this problem.
What you are trying to do is ‘working against the system’ - you are effectively making two save buttons. In the rest of Crm when the Id is required for a ribbon button the record must first be saved. E.g. you can’t use the dialog or workflow buttons on an unsaved record, you also can’t 'add new/existing' to an unsaved record.
So my solution would be to disable the button on unsaved forms, force the user to save the record manually and then allow them to use your button - this is the way Crm is meant to be used, and is the way the rest of Crm will work.
You should not work against the system, you should work with it, you have a product to customise and extend – not change.
If this doesn’t meet your requirement I would suggest uses Greg’s suggestion (1) of having flags, though it sounds a bit messy - but then this is a requirement that inherently is.

You could try one of two things:
Add a hidden boolean attribute to your form(e.g. "new_launchProductCreate"), set it in code prior to save and then read it onLoad.
Instead of setting the value prior to create (and therefore potentially commiting it to the database), you could create a plugin registered against the "Create" step of your record that injects a boolean value into the Entity.Attributes collection as the record is returned to the user. This would prevent the value persisting into the database and running every time your form loads.
You can instead use AJAX to reset the value as you launch your onLoad code so that it doesn't trigger on every form load
Assign the record guid manually, use AJAX to save your record, pop your new window using th enew guid and then reload your original form (so that the form is no longer in an "unsaved" state).

At the risk of being proven wrong as I cannot verify this right away... you will need to save and then reload the page.
The value stored in Xrm.Page.data.entity.getId() is set when the page is loaded/initialised and hence won't be updated when you access it after you have called Save().
It is also why it does work when you reload the page.
Perhaps you could call save and then reload the window adding a querystring variable of your own, to indicate that this event has just occurred?
e.g.
function DoSomething() {
//do your stuff
Xrm.Page.data.entity.save();
//something like - sure someone can do better!
window.location = window.location.href + '&foo=bar';
}
and then register something like this onFormLoad
function OnLoad() {
var queryStringParms = Xrm.Page.context.getQueryStringParameters();
//test to see if your query string param exists here
for (var i in queryStringParams) {
//if you find query string, do extra processing here
}
}

Related

How to get query sys_id of current.sys_id Service Portal (ServiceNow)

I have a question regarding a small issue that I'm having. I've created a widget that will live on the Service Portal to allow an admin to Accept or Reject requests.
The data for the widget is pulling from the Approvals (approval_approver) table. Under my GlideRecord, I have a query that checks for the state as requested. (Ex. addQuery('state', 'requested'))
To narrow down the search, I tried entering addQuery('sys_id', current.sys_id). When I use this query, my script breaks and I get an error on the Service Portal end.
Here's a sample of the GlideRecord script I've written to Accept.
[//Accept Request
if(input && input.action=="acceptApproval") {
var inRec1 = new GlideRecord('sysapproval_approver');
inRec1.addQuery('state', 'requested');
//inRec1.get('sys_id', current.sys_id);
inRec1.query();
if(inRec1.next()) {
inRec1.setValue('state', 'Approved');
inRec1.setValue('approver', gs.getUserID());
gs.addInfoMessage("Accept Approval Processed");
inRec1.update();
}
}][1]
I've research the web, tried using $sp.getParameter() as a work-around and no change.
I would really appreciate any help or insight on what I can do different to get script to work and filter the right records.
If I understand your question correctly, you are asking how to get the sysId of the sysapproval_approver record from the client-side in a widget.
Unless you have defined current elsewhere in your server script, current is undefined. Secondly, $sp.getParameter() is used to retrieve URL parameters. So unless you've included the sysId as a URL parameter, that will not get you what you are looking for.
One pattern that I've used is to pass an object to the client after the initial query that gets the list of requests.
When you're ready to send input to the server from the client, you can add relevant information to the input object. See the simplified example below. For the sake of brevity, the code below does not include error handling.
// Client-side function
approveRequest = function(sysId) {
$scope.server.get({
action: "requestApproval",
sysId: sysId
})
.then(function(response) {
console.log("Request approved");
});
};
// Server-side
var requestGr = new GlideRecord();
requestGr.addQuery("SOME_QUERY");
requestGr.query(); // Retrieve initial list of requests to display in the template
data.requests = []; // Add array of requests to data object to be passed to the client via the controller
while(requestsGr.next()) {
data.requests.push({
"number": requestsGr.getValue("number");
"state" : requestsGr.getValue("state");
"sysId" : requestsGr.getValue("sys_id");
});
}
if(input && input.action=="acceptApproval") {
var sysapprovalGr = new GlideRecord('sysapproval_approver');
if(sysapprovalGr.get(input.sysId)) {
sysapprovalGr.setValue('state', 'Approved');
sysapprovalGr.setValue('approver', gs.getUserID());
sysapprovalGr.update();
gs.addInfoMessage("Accept Approval Processed");
}
...

How can I determine the revision of a calendar item in EWS when using a PullSubscription

I am trying to do some synchronization work with and Exchange Calendar. I want to keep another external calendar in sync with Exchange. Currently when the other app triggers a creation or update of some sort in Exchange, that change is then sent back to the other calendar creating an endless loop.
I had hoped to use the AppointmentSequenceNumber property when binding the Appointment item, but it always has the value of 0 no matter how many times it is updated. I am including AppointmentSequenceNumber in my PropertySet.
If anyone knows of a way to catch these updates and keep them from being sent back, that would be very helpful.
Thank you.
PropertySet pSet = new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.Subject, ItemSchema.Body, AppointmentSchema.Start, AppointmentSchema.End,AppointmentSchema.ArchiveTag, AppointmentSchema.InstanceKey, AppointmentSchema.AppointmentSequenceNumber);
ChangeCollection<ItemChange> changes = null;
.....
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013)
{
Url = new Uri(exInfo.ServiceURL),
Credentials = new WebCredentials(exInfo.UserName, exInfo.Password)
};
//Pull Subscription Info
Microsoft.Exchange.WebServices.Data.PullSubscription sub = service.SubscribeToPullNotifications(
new FolderId[] { WellKnownFolderName.Calendar }, 30, "",
EventType.Created, EventType.Modified, EventType.Deleted);
syncState = exInfo.SyncState;
//Pull Changes
while (!syncComplete )//&& Count < MaxItems)
{
changes = service.SyncFolderItems(new FolderId(WellKnownFolderName.Calendar),
PropertySet.FirstClassProperties, null, 100, SyncFolderItemsScope.NormalItems, syncState);
foreach (ItemChange change in changes)
{
if (change.ChangeType != ChangeType.Delete) { eventItem = Appointment.Bind(service, change.ItemId, pSet); }
switch (change.ChangeType)
{
case ChangeType.Update:
...
break;
case ChangeType.Create:
...
break;
case ChangeType.Delete:
...
break;
}
Count++;
}
syncState = changes.SyncState;
syncComplete = !changes.MoreChangesAvailable;
}...
The AppointmentSequenceNumber would only be valid for Meetings; on normal Appointments it isn't used.
I had hoped to use the AppointmentSequenceNumber property when binding the Appointment item
That wouldn't work even if it was incrementing. Exchange will always provide you with the current version and the only things valid in a Bind is the EWSId of the appointment (or the Recurrence Sequence).
If anyone knows of a way to catch these updates and keep them from being sent back, that would be very helpful.
Synchronization is complicated but (from a notification perspective) if you modify an item in Exchange it's going to fire a notification and the ChangeKey attribute on the Item will be updated (quote):
"When you work with items in Exchange, another value to keep in mind is the ChangeKey attribute. This value, in addition to the item ID, is used to keep track of the state of an item. Any time an item is changed, a new change key is generated. When you perform an UpdateItem operation, for example, you can use the ChangeKey attribute to let the server know that your update is being applied to the most current version of the item. If another application made a change to the item you’re updating, the change keys won’t match and you will not be able to perform the update."

Kendoui grid destroy method doesn't refresh grid

I have a kendoui grid (no server-side wrappers). It has several columns displayed and "destroy" (delete) button. Everything works just fine. But it two issues:
When I click "delete" button, then a request to a server is sent (a record is sucessfully deleted on the server). I return a new list of records from the server (after processing a "delete" request). But this new list of records is ignored and not used on the client. And I have to use "requestEnd" grid event to refresh the grid. Hence two HTTP requests are made to the server: "delete a record", "load new list of records". Is it possible to delete a record and return a new list of records from the server usign one single HTTP request?
Also when I click "delete" button the appropriate grid row is immediatelly deleted (user-interface only), then a HTTP request is sent to the server. Is it possible to delete the record from the grid (UI) only after HTTP request is processed?
P.S. Previously when I used Telerik MVC Extensions (before kendoui) it worked fine.
Destroy call is executed after the item is removed from the datasource.data() collection, therefore there is no need to return list. If your delete somehow effect other items in the list, call datasource.read form destroy.complete (but yes these are two calls).
At the other hand, nothing is stopping you from returning complex json object as a result of delete, where one of the properties would be your list, unwrapping it on destroy.complete and assigning property with the collection to the datasource, doable. Although consider paging, sorting and other features you will have to deal with if you decide to take it this way.
It is, you can implement custom command and implement the delete process as you wish, using the api.
Examples :
A) pagination and filters down to the server, assuming you enabled server side paging.
parameterMap: function (o, operation) {
var output = null;
switch (operation) {
case "create":
break;
case "read":
output = '{ filter: ' + JSON.stringify(o) + '}';
break;
}
return output;
}
},
B) Creating complex json is server side platform specific, in theory you wrap the actual json you are about to return to the client and wrap it into other json, add extra properties and then return it. You can then read it in a following way:
transport: {
destroy: {
complete: function (jqXhr, textStatus) {
var result = jQuery.parseJSON(jqXhr.responseText);
var yourdata = result.yourdata
// pass your data to datasource
}
}
}

Logout change view sencha touch

Hi I need to implement keep login in my sencha touch application
Please see my code below:
Login.js - Once user click login, it will store "sessionToken" in local storage.Then it will go to main Page
onBtnLoginClick: function(){
var loginviewGetValue = Ext.getCmp('loginview').getValues();
var bbid = Ext.getCmp('bbID').getValue();
var bbpassword = Ext.getCmp('bbPassword').getValue();
var LoginLS = Ext.getStore('LoginLS');
LoginLS.add({
sessionId: 'sadsadsadasd'
,deviceId:'1'
,bb_id :bbid
});
LoginLS.sync();
var mainForm= Ext.create('bluebutton.view.Main');
Ext.Viewport.setActiveItem(mainForm);
App.js~ Everytime launch function will check sessionToken in localStorage. If Localstorage is empty then it will go to login page.Else it will go to main Page
launch: function() {
// Destroy the #appLoadingIndicator element
Ext.fly('appLoadingIndicator').destroy();
// Initialize the main view
var LoginLS = Ext.getStore('LoginLS');
LoginLS.load();
var record = LoginLS.getAt(0);
if(record != undefined){
var sessionId = record.get('sessionId');
if (sessionId !=undefined){
Ext.Viewport.add(Ext.create('bluebutton.view.Main'));
}
else
Ext.Viewport.add(Ext.create('bluebutton.view.Login'));
}
else{
Ext.Viewport.add(Ext.create('bluebutton.view.Login'));
}
// Ext.create('bluebutton.view.TopMenuList');
},
Logout.js~Logout will clear the sessionToken and go to login page again
onLogoutClick: function scan() {
var LoginLS = Ext.getStore('LoginLS');
Ext.Viewport.setMasked({
xtype: 'loadmask',
message: 'Loading...'
});
LoginLS.load();
var record = LoginLS.getAt(0);
LoginLS.removeAll();
LoginLS.sync();
//Load a new view
// Ext.getCmp('tabpanel').destroy();
var loginForm = Ext.create('bluebutton.view.Login');
Ext.Viewport.setActiveItem(loginForm);
Ext.Viewport.setMasked(false); // hide the load screen
But i having a problem now. I not able to go back login page. It go to the blank page. Please give me some solution. Thanks.
Here is the error i get
[WARN][Ext.data.Batch#runOperation] Your identifier generation strategy for the model does not ensure unique id's. Please use the UUID strategy, or implement your own identifier strategy with the flag isUnique. Console.js:35
[WARN][Ext.Component#constructor] Registering a component with a id (`loginview`) which has already been used. Please ensure the existing component has been destroyed (`Ext.Component#destroy()`. Console.js:35
[WARN][Ext.Component#constructor] Registering a component with a id (`bbID`) which has already been used. Please ensure the existing component has been destroyed (`Ext.Component#destroy()`. Console.js:35
[WARN][Ext.Component#constructor] Registering a component with a id (`bbPassword`) which has already been used. Please ensure the existing component has been destroyed (`Ext.Component#destroy()`. Console.js:35
[WARN][Ext.Component#constructor] Registering a component with a id (`btnLogin`) which has already been used. Please ensure the existing component has been destroyed (`Ext.Component#destroy()`. Console.js:35
[DEPRECATE][bluebutton.view.Login#show] Call show() on a component that doesn't currently belong to any container. Please add it to the the Viewport first, i.e: Ext.Viewport.add(component);
Looking at the error messages it is clear that you are trying to create login panel again without destroying existing component. Error comes because you are not allowed to use same id more than once in application.
To avoid this you should not create same view multiple times, you should reuse views which is good for performance also. One more thing, you should give id to n element if and only if you can't do without it.
Assuming you cannot avoid id attributes you should do one of these 2 things:
Create new view only if it doesn't exist
var loginView = Ext.getCmp("loginview");
if(!loginView){
loginView = Ext.create('bluebutton.view.Login');
}
destroy login view as soon as it is out(hide/erased) of viewport by calling:
var loginView = Ext.getCmp("loginview");
loginView.destroy();
Use itemId for your components instead of idand reference them accordingly in you Controller. Check this question out: Warning saying `Id` exist and should be destroyed

windows phone c# check for valid url and replace foreach item in list

I am getting a list of objects in Windows Phone, and show them in a listbox with databinding.
some image urls are not valid, so after every object is added in the list, i run the following code to check and replace, if not valid
private void CheckLinkUrl(Person p)
{
Uri filePath = new Uri(p.img_url);
string correct = p.img_url;
HttpWebRequest fileRequest = HttpWebRequest.CreateHttp(filePath);
fileRequest.Method = "HEAD";
fileRequest.BeginGetResponse(result =>
{
HttpWebRequest resultInfo = (HttpWebRequest)result.AsyncState;
HttpWebResponse response;
try
{
response = (HttpWebResponse)resultInfo.EndGetResponse(result);
}
catch (Exception e)
{
p.img_url = "http://somethingelse.com/image.jpg";
}
}, fileRequest);
}
the problem is that it is very slow, it takes sometimes 2 minutes+ to load every image (although the UI remains responsive, and everything else is displayed immediately in the listbox, apart from the images)
am I doing something wrong? can i get it to run faster?
EDIT:
I tried using the imagefailed event and replace the link, no improvement at the speed of loading the pics
What I have done to avoid this problem in my application is, I have loaded the items with a default Image, The image source is binded to a property in my result item of type ImageSource. By default it returns the default image. After processing or download completion the imagesource value changes to the new Image triggering the NotifyPropertyChanged event and hence it is automatically reflected on the UI. I hope it helps you.

Resources