How to execute workflows in code? - sitecore

Scenario:
User clicks a command on the workflow
Workflow custom action carry out a number of checks
Workflow custom action executes another command on the same workflow dependant on results
The code I have so far is:
Database db = Factory.GetDatabase("master");
if (Request.QueryString["_id"] != null)
{
var itm = db.GetItem(new ID(Request.QueryString["_id"]));
WorkflowCommand[] availableCommands = wf.GetCommands(itm.Fields["__Workflow state"].Value);
wf.Execute(Request.QueryString["command"], itm, "Testing working flow new screens", false, new object[] { }); // Execute the workflow step.
}
However, I get a Object not set to an instance error on the wf.Execute line - but with no meaningful stack trace or anything :(
I've put in the wf.GetCommands line just to check that things are actually where I expect them, and availableCommands is populated with a nice list of commands that exist.
I've checked the commandId is valid, and exists.
Itm is not null, and is the Content Item that the workflow is associated to (that I want the workflow to run in context with).
I've checked that the user context etc is valid, and there are no permission issues.
The only difference is that I am running this code within an .aspx page that is executing within sitecore - hopeever, I wouldn't have expected this to cause a problem unless there is a context item that isn't being set properly.

Workflow needs to be run within a SiteContext that has a ContentDatabase and workflow enabled. The easiest way to do this within your site is to use a SiteContextSwitcher to change to the "shell" site.
using (new SiteContextSwitcher(SiteContextFactory.GetSiteContext("shell")))
{
wf.Execute(Request.QueryString["command"], itm, "Testing working flow new screens", false, new object[] { }); // Execute the workflow step.
}
An example of this can be found within the code for the WeBlog Sitecore module.
http://svn.sitecore.net/WeBlog/Trunk/Website/Pipelines/CreateComment/WorkflowSubmit.cs

Related

Different results when making a Corda query via API and inside a flow

I’m getting some strange behaviour. When I update a state with a list of partner ids - other nodes - and and read the state afterwards it seems that via rpcOps.vaultQueryBy I can see the updated - or unconsumed - state with the updated list of partners, but if I do same query via serviceHub.vaultService.queryBy it looks like the state’s parner list hasn’t changed at all.
If I get all states in the flow - also the consumed - it looks like there has not been a change, but via API all updates into partners list are visible. Is this some sort of a bug I have encountered or am I just not understanding something?
We're using Corda 4.0.
Via API
var servicestates = rpcOps.vaultQueryBy<ServiceState>().states.map { it.state.data }
var services = getServices().filter {
it.linearId == UniqueIdentifier.fromString(serviceId)
}.single()
Inside flow
val serviceStateAndRef = serviceHub.vaultService.queryBy<ServiceState>(
QueryCriteria.LinearStateQueryCriteria(linearId = listOf(serviceLinearId))
).states.single()
#Ashutosh Meher You got it near enough. The problem was in a previous flow, where, when creating a new partner state the command call for contract, there was only the caller listed.
So
Command(ServiceContract.Commands.AddPartner(),listOf(ourIdentity.owningKey))
had to be edited to include necessary other parties.
Command(ServiceContract.Commands.AddPartner(),updatedServiceState.participants.map { it.owningKey })
That resulted the other node not to see the change. It was right under my eyes all the time... ;)

Form loads twice and cancels my load event logic

I'm trying to get the "Get Products" ribbon action logic in the main Invoice form to be executed automatically when the form is in update mode.
The invoice is created through a business flow, that starts with an Opportunity.
Here's the code form the load event of the invoice onLoad event:
onFormLoad = function () {
//if we are in update mode...
if (Xrm.Page.ui.getFormType() == 2) {
//if no products are present in the list...
if (loadInvoiceProducts().length == 0) {
Mscrm.CommandBarActions.getProducts();
}
}
};
loadInvoiceProducts = function () {
//just a jQuery ajax call to the oData service... this works.
var products = oDataQuery("InvoiceDetailSet?$filter=InvoiceId/Id eq guid'" + Xrm.Page.data.entity.getId() + "'");
return products.results;
};
This works fine if I manually create a new order (the form is then in form mode == 1, create) and when I fill in the required fields and save, the form reloads in update mode, then the "Get Products" popup appears.
The problem is when the invoice is created through the business flow. The invoice form opens in create mode (through the business flow, all the required fields are already filled) and when I hit Ctrl-S, the code above is triggered, the form is in update mode, but then another refresh happens and then the code above is not run.
I have to hit F5 for it to be triggered again.
Has anyone attempted anything like this before ?
Thanks.
Recent versions of CRM have asynchronous form loading and refreshing, which is likely what you're running into. When a new record is created and saved, onload is triggered again, as you've noted. When an existing record is updated and saved, onload is not triggered again. To learn more about what's going on, add an onsave handler that cancels the save, like this:
// Put this function into your script
function cancelSave(econtext) {
var eventArgs = econtext.getEventArgs();
console.log("saveMode", eventArgs.getSaveMode()); // could help you figure out why form is saving
eventArgs.preventDefault();
}
// And then put this line into your onload method
Xrm.Page.data.entity.addOnSave(cancelSave);
If after adding the handler your issue goes away, then the problem is that your existing record is being saved which does not trigger onload again as I mentioned. You will need to investigate why the form is saving:
Do you have other code that could be triggering a save?
If the console output from cancelSave shows 70 ("AutoSave"), it is auto-save (which you can disable system-wide or on your form specifically [search the sdk for preventAutoSave])
If the console output from cancelSave shows 2 ("Save and Close"), then something on your form might be causing navigation to occur (when auto-save is enabled, navigating away from a form with unsaved data triggers a save with mode 2)
If you determine that it is a save event that is interfering but can't figure out where the save is coming from for some reason, then you can also take the approach of figuring out which form fields are dirty. Save events do nothing if there are no dirty fields, so if you could figure out and resolve the dirtiness, that would work around the problem. One easy way to see which fields are dirty is to enable auditing on the entity and then view the audit log to see which fields were changed with the save.

How to get all goals triggered during Sitecore session in commitDataSet Analytics pipeline?

I have an Analytics pipeline added just before the standard one in section to delete duplicate triggered pageevents before submitting all to database so I can have unique triggered events as there seems to be a bug on android/ios devices that triggers several events within few seconds interval.
In this custom pipeline I need to get the list of all goals/events the current user triggered in his session so I can compare with the values in dataset obtained from args parameter and delete the ones already triggered.
The args.DataSet.Tables["PageEvents"] only returns the set to be submitted to database and that doesn't help since it is changing each time this pipeline runs. I also tried Sitecore.Analytics.Tracker.Visitor.DataSet but I get a null value for these properties.
Does anyone knows a way how to get a list with all goals the user triggered so far in his session without requesting it directly to the database ?
Some code:
public class CommitUniqueAnalytics : CommitDataSetProcessor
{
public override void Process(CommitDataSetArgs args)
{
Assert.ArgumentNotNull(args, "args");
var table = args.DataSet.Tables["PageEvents"];
if (table != null)
{
//Sitecore.Analytics.Tracker.Visitor.DataSet.PageEvents - this list always empty
...........
}
}
}
I had a similar question.
In Sitecore 7.5 I found that this worked:
Tracker.Current.Session.Interaction.Pages.SelectMany(x=>x.PageEvents)
However I'm a little worried that this will be inefficient if the Pages collection is very large.

Can I guarantee publishing of an item I've just created?

I've got a situation where I want end-users to be able to create new Sitecore items... and then immediately be able to navigate to the Item's URL. Obviously the item will have to be published... but this happens in something of a black box. Is there a way to guarantee an item has been published from Master to Web?
Alternately, I could create the Item in the Web DB... then re-create/copy a Master version at some point. But this strategy seems fraught with peril. And maybe just a bad idea in general.
Suggestions? Am I being needlessly paranoid about creating items directly in Web?
I'll start by saying my answer is not necessarily advisable. However, depending on your situation it should work.
If the items that users are creating always have the same template you could try creating a custom item resolver that falls back to using the Master database.
Allow Sitecore to attempt to resolve the item normally.
When it fails, look in the Master database.
If the item is in there, make sure it has the correct template
Set the found item as the context item.
Using this method, you can publish the items from Master->Web s normal, but you don't have to wait until publishing is completed to see it.
Once again, this should solve the problem, but it's up to you to weigh the risks of serving up Master DB content.
You can't "guarantee" because publishing may be queued and won't go live instantly. Instead if you need instant access, I recommend a preview site that points to the master database:
How to Setup a Sitecore Preview Site to Review Content Before Publishing
You could create a event mapping to a class with the code to publish the item. In the code you can define any rule you want regarnding whether to publish or not.
<event name="item:saved">
<handler type="YourType.ItemEventHandler, YourType" method="OnItemSaved" />
</event>
And the OnItemSaved would look like this: (not tested)
protected void OnItemSaved(object sender, EventArgs args)
{
if (args == null)
{
return;
}
Item item = Event.ExtractParameter(args, 0) as Item;
var webDb = Sitecore.Configuration.Factory.GetDatabase("web");
var masterDb = Sitecore.Configuration.Factory.GetDatabase("master");
foreach (Language language in masterDb.Languages)
{
var options = new PublishOptions(masterDb, webDb, PublishMode.SingleItem, language, DateTime.Now)
{ RootItem = item, Deep = true };
var myPublisher = new Publisher(options);
myPublisher.Publish();
}
}
And about creating the item in the web db, I also wouldn`t go that way. It would be replaced by the default publishing process unexpectedly.
Personally i don't like front-end user creating items in master database, i feel only content authors/editors should be the ones who create items from either content editor or page editor.
There is no guarantee the item gets published instantly, I would recommend you to store any user-created data in a separate database and on the redirect URL just read the data from this database.

SharePoint List item BRIEFLY appears as edited by 'System Account' after item.update

I have a shopping cart like application running on SharePoint 2007.
I'm running a very standard update procedure on a list item:
using (SPWeb web = site.OpenWeb())
{
web.AllowUnsafeUpdates = true;
SPList list = web.Lists["Quotes"];
SPListItem item = list.GetItemById(_id);
item["Title"] = _quotename;
item["RecipientName"] = _quotename;
item["RecipientEmail"] = recipientemail;
item["IsActive"] = true;
item.Update();
site.Dispose();
}
This item updates properly, however it briefly appears as modified by System Account. If I wait a second and refresh the page, it shows up again as modified by CurrentUser.
This is an issue because on Page_Load I am retrieving the item that is marked as Active AND is listed as Modified By the CurrentUser. This means as a user updates his list, when the PostBack finishes, it shows he has no active items.
Is it the web.AllowUnsafeUpdates? This is necessary because I was getting a security error before.
What am I missing?
First off, it's not AllowUnsafeUpdates. This simply allows modifying of items from your code.
It's a bit hard to tell what's going on without understanding more of the flow of your application. I would suggest though that using Modified By to associate an item to a user may not be a great idea. This means, as you have discovered, that any modification by the system or even potentially an administrator will break that link.
I would store the current user in a custom field. That should solve your problem and would be a safer design choice.
There could be some other code running in Event Receivers and updating the item. Because event recievers runs in context of system user account, and if you update item from event reciever, the modified field will show just that: the system account has modified the item.