Publishing failing in final stage of workflow - tridion

Our final automatic activity is publishing component to live target. We have below code written in Edit script.
' Script for Automatic Activity Content Manager Workflow
Set oTDSE = CreateObject("TDS.TDSE")
Call oTDSE.Initialize
Set oWorkItem = CurrentWorkItem.GetItem(3)
sDestinationServer = "tcm:0-18-65538"
Set oComp = oTDSE.GetObject(oWorkItem.ID, 3)
Call oComp.Publish(sDestinationServer, True, True, True)
FinishActivity "Automatic Activity ""Process Complete"" Finished"
set oWorkItem = Nothing
set oComp = Nothing
set oTDSE = Nothing
This code is executing successfully but when we check publishing queue component is getting failed with error The item tcm:34-20615-16-v0 does not exist.
Same code is working fine when we are publishing the component to staging in earlier activity.

The problem is that while in the script you are publishing dynamic version (-v0) of component. As publishing is asynchronous operation, item is not published straightaway, but publish transaction is created (which is linking to dynamic version).
After this, your script is done and item get checked-in. Now, publisher is starting with processing your publish transaction and discovers that there's no dynamic version anymore, hence your exception.
When publish activity is not last, publisher has enough time to get dynamic version of an item.
Workaround can be to wait for publish transaction to complete in your automatic activity, or do something with OnCheckIn event

Yes this is frequent one I faced at clients place. Especially when your last activity in workflow is automatic and Publish to Live.
Simplest way I did is:
First FinishActivity in automatic code
Then Publish workflow=false.
PublishCoreServiceClient.FinishActivity(activityInstance.Id, finishData, publishoptions);
}
//Now Publish
ComponentData component = (ComponentData)PublishCoreServiceClient.Read(componentid, publishoptions);
if (GetConfigurationValues(component, PublishCoreServiceClient))
{
PublishInstructionData publishInstructionData = new PublishInstructionData();
publishInstructionData.MaximumNumberOfRenderFailures = 100;
publishInstructionData.RollbackOnFailure = true;
ResolveInstructionData resolveInstructionData = new ResolveInstructionData();
resolveInstructionData.IncludeWorkflow = false;
resolveInstructionData.IncludeChildPublications = true;
resolveInstructionData.IncludeComponentLinks = true;
publishInstructionData.ResolveInstruction = resolveInstructionData;
RenderInstructionData renderInstructionData = new RenderInstructionData();
publishInstructionData.RenderInstruction = renderInstructionData;
List<string> ItemToPublish = new List<string>();
ItemToPublish.Add(component.Id);
if (!String.IsNullOrEmpty(Utilities.LIVE_URI))
{
PublicationTargetData pubtarget = (PublicationTargetData)PublishCoreServiceClient.Read(Utilities.LIVE_URI, publishoptions);
List<string> target = new List<string>();
target.Add(pubtarget.Id);
PublishCoreServiceClient.Publish(ItemToPublish.ToArray(), publishInstructionData, target.ToArray(), PublishPriority.Normal, publishoptions);
Logger.Debug("ElapsedMilliseconds Publish [" + _watch.ElapsedMilliseconds + " ms]");
}

Issue is resolved after setting activateWorkflow parameter of Publish method to False.

We had the same error and we have solved it by sending the component to publish with a few seconds delay:
Call oComp.Publish("tcm:0-1-65538", False, False, True, dateAdd("s",10,Now))

Related

Redirect all ASP.NET Core logging into a single NLog logger

I have an ASP.NET project that sends its logs to NLog.
However in this project, I have my own NLog logger and I would like to know how to route all the logs through it.
I guess I shouldn't add NLog as a logger, but I should find a way to register a method that will get called each time ASP tries to log anything.
How can this be accomplished?
This is the code that creates the logger:
// create the module name
var ProcessName = Process.GetCurrentProcess().ProcessName;
_ModuleName = ProcessName + " (\"" + Oracle.GuessMyName() + "\")";
// create the logger configuration
var Configuration = new LoggingConfiguration();
// create the file target
var FileTarget = new FileTarget ("file")
{
FileName = #"x:\Logs\${processname}.log",
ArchiveFileName = #"x:\Logs\${processname}.{#}.log",
Layout = #"${longdate}|${logger}|${level}|${message}${onexception:|Exception occurred:${exception:format=tostring}${newline}",
ArchiveEvery = FileArchivePeriod.Day,
ArchiveNumbering = ArchiveNumberingMode.Rolling,
MaxArchiveFiles = 7,
ConcurrentWrites = true
};
Configuration.AddTarget(FileTarget);
// create the viewer target
var ViewerTarget = new NLogViewerTarget ("viewer")
{
Layout = #"${message}${onexception:${newline} --> Exception occurred\:${exception:format=tostring}",
IncludeSourceInfo = true,
IncludeCallSite = true,
Address = #"udp://127.0.0.1:9999"
};
Configuration.AddTarget(ViewerTarget);
// set the rules
Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, FileTarget));
Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, ViewerTarget));
// set the configuration
LogManager.Configuration = Configuration;
// create a new logger
_Logger = LogManager.GetLogger(_ModuleName);
and this is also how ASP.net gets attached to nlog:
LoggerFactory.AddNLog();
Application.AddNLogWeb();
Now the current log layout looks like this for two process (the animal names are automatically changing every time the process is restarted)
so both process: shinobi and mouserun here have their own log output, but anything ASP related goes to ASP's nlog instance called Microsoft, regardless of the process.
the goal is to have the ASP output of shinobi to go in the shinobi logger and the mouserun ASP output to go in the mouserun logger.
Look at the code of NLog.Extensions.Logging, where it injects its own custom log-provider.
You can do the same and just wrap your global-logger object:
https://github.com/NLog/NLog.Extensions.Logging/blob/e48d6cc54d9abd70d976066265c7992117cbac5a/src/NLog.Extensions.Logging/NLogLoggerProvider.cs
https://github.com/NLog/NLog.Extensions.Logging/blob/1474ffe5b26d2ac95534ed01ef259133133bfb67/src/NLog.Extensions.Logging/NLogLoggerFactory.cs
https://github.com/NLog/NLog.Extensions.Logging/blob/2c05a4fbdda0fe026e60814d535e164e18786aef/src/NLog.Extensions.Logging/ConfigureExtensions.cs
public static ILoggerFactory AddNLog(this ILoggerFactory factory, NLogProviderOptions options)
{
ConfigureHiddenAssemblies();
using (var provider = new NLogLoggerProvider(options))
{
factory.AddProvider(provider);
}
return factory;
}
You could also create a custom-target, and redirect all non-global-logger messages to this target using NLog rules:
https://github.com/nlog/NLog/wiki/Configuration-file#rules
The custom target can then just forward the log-event to the global-logger:
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target
You should be careful with cyclic logging. Maybe have a filter in the custom-target to ignore messages from the global-logger.
But I think this is an ugly solution, and I fail to understand the restriction of only one logger-object. Especially when the reason is because it should be named after the application. Why not not a global variable for the name instead of abusing the logger-name?
Alternative you can create a custom target wrapper, that fixes the Logger on LogEventInfo's, so when forwarded to the wrapped target (UDP- / File-target), then it looks like they are all come from the same logger.
Similar to what this guy is trying to do:
https://github.com/NLog/NLog/issues/2352
Again really ugly solution, and should only be used when not able to figure out, how to avoid using the logger-name in the configuration of the wanted Nlog-targets (Ex. configure file-target-filename using something else).

WF4 Ready Instances

How can I find out what items in the database are ready to be ran. In other words, I want to query the persistence tables to identify what items have the lock that expired. I can't seem to find any fields that would show this.
I've never worked it out either.
I've used workflow.Load and caught the exception; which is nasty but worked.
On an WorkflowApplication instance, you can use the LoadRunnableInstance method which automatically loads the next available workflow in the instance store whose lock has expired.
AutoResetEvent sync = new AutoResetEvent(false);
Workflow1 myWorkflow = new Workflow1();
SqlWorkflowInstanceStore instanceStore = new SqlWorkflowInstanceStore("<my connection string>");
WorkflowApplication wfApp = new WorkflowApplication(myWorkflow);
wfApp.InstanceStore = instanceStore;
wfApp.Completed += (eventArgs) => sync.Set();
wfApp.LoadRunnableInstance();
wfApp.Run();
sync.WaitOne();

How do I get a list of users with the Core Service?

I'm trying to get a list of the available users from the Core Service. I spend quite some time looking at the available service methods and the most obvious seemed to be this:
TrusteesFilterData trusteesFilterData = new TrusteesFilterData
{
BaseColumns = ListBaseColumns.IdAndTitle,
IsPredefined = false,
ItemType = ItemType.User
};
XElement listTrustees = client.GetSystemWideListXml(trusteesFilterData);
However, the code throws an error when calling GetSystemWideListXml - Unable to create Abstract Class. Am I using the correct approach and, if so what am I doing wrong? If not, what should I be doing instead?
Take a look at the samples in the open source project for workflow notification
http://code.google.com/p/tridion-notification-framework/source/browse/NotificationService/NotificationService/Worker.cs
Lines 22 - 26 in the DoWork() method should do what you need - I think need to use UsersFilterData rather than TrusteesFilterData
var users = client.GetSystemWideList(new UsersFilterData { BaseColumns = ListBaseColumns.IdAndTitle, IsPredefined = false });

Tridion 2011 Sp1 allow to publish revision version of the component to staging target

Here is a scenario: I have a Page which have Component A. Component A have few linked components B and C. If Editor modifies component B and want to publish to staging target while component still in the workflow so Reviewer can view the changes on staging server before approve component B. When editor preview component he can see the changes, but when he publish to staging target it will grab the last checked in version of the Component A, which still linked to the unmodified version of Component B. How to pragmatically overwrite the default behavior to allow Editor publish his changes to staging environment before completing activity for the item?
Also, when component B inserted directly on the second page I was able to publish from VBScript from workflow automated activity using the following:
Dim strItemURI
strItemURI = CurrentWorkItem.GetItem(2).ID
Dim oComp
Set oComp = TDSE.GetObject(strItemURI, 1)
Call oComp.Publish("tcm:0-13-65537", True, True, False)
Set oComp = Nothing
FinishActivity "Automatic Activity ""Publish to Staging"" Finished"
Do I need to write custom resolver to accomplish above scenario to allow modified version of linked components published to staging environment while in workflow?
Any idea or samples will be appreciated.
Thanks.
Updated:
I'm trying to create TBB, which will replace modified version of the item in the package. Any idea on this? Here is some code:
public void Transform(Engine engine, Package package)
{
try
{
_publicationID = engine.PublishingContext.ResolvedItem.Item.Id.PublicationId;
string stagingTarget = Settings.GetSetting("StagingTargetUri");
PublicationTarget target = new PublicationTarget(new TcmUri(stagingTarget), engine.GetSession());
if(engine.PublishingContext.ResolvedItem.PublicationTarget!=null){
if (stagingTarget.Contains(engine.PublishingContext.ResolvedItem.PublicationTarget.Id.ToString()))
{
foreach (Item item in package.GetAllByType(ContentType.Component))
{
VersionedItem versionedItem = (VersionedItem)engine.GetObject(item);
if (versionedItem.LockType.HasFlag(LockType.InWorkflow))
{
Component componentInWorkflow =
(Component)engine.GetObject(new TcmUri(versionedItem.Id.ItemId, versionedItem.Id.ItemType, versionedItem.Id.PublicationId, 0));
package.Remove(item);
Item mainComponent= package.CreateTridionItem(ContentType.Component,componentInWorkflow);
package.PushItem(mainComponent);
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
According to documentation:
You can publish an item in Workflow if it meets the minimum approval
status set for the Publishing Target. If the item is in Workflow and
does not meet the minimum approval status, the Content Manager
publishes the last checked-in version of the item.
This means that you need to:
Set the Minimum Approval Status on your Publication target as something like "Staging"
As a first step on your Workflow set the Approval Status for your component to "Staging"

start workflow using alfresco java script api or through web script

I want to start a workflow programatically. So written a web script.
Execute Script :
function startWorkflow()
{
var workflow = actions.create("start-workflow");
workflow.parameters.workflowName = "activiti$alfGroupReview";
workflow.parameters["bpm:workflowDescription"] = "Please review ";
workflow.parameters["bpm:groupAssignee"] = people.getGroup( "GROUP_site_collaborators");;
var futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 7);
workflow.parameters["bpm:workflowDueDate"] = futureDate;
workflow.execute(document);
return ;
}
For the above script, I am getting error "document is not defined". I am referring https://forums.alfresco.com/en/viewtopic.php?f=34&t=42677 and http://livinginjava.blogspot.in/2008/10/starting-alfresco-workflow-using.html links.
So I update my script to :
function startWorkflow()
{
var nodeRef = "workspace://SpacesStore/25285e6c-2995-49fe-aa50-1270cefc806a";
var docNode = search.findNode(nodeRef);
var workflow = actions.create("start-workflow");
workflow.parameters.workflowName = "activiti$alfGroupReview";
workflow.parameters["bpm:workflowDescription"] = "Please review ";
workflow.parameters["bpm:groupAssignee"] = people.getGroup( "GROUP_aloha_collaborators");;
var futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 7);
workflow.parameters["bpm:workflowDueDate"] = futureDate;
workflow.execute(docNode);
return ;
}
Here, nodeRef : is ref of a document from document library.
Now new error is :
500 Description: An error inside the HTTP server which prevented it from fulfilling the request.
Message: 06270056 Wrapped Exception (with status template): 06270273 Failed to execute script 'classpath*:alfresco/templates/webscripts/org/justransform/startWF.get.js': null
Exception: org.alfresco.scripts.ScriptException - 06270273 Failed to execute script 'classpath*:alfresco/templates/webscripts/org/justransform/startWF.get.js': null
org.alfresco.repo.jscript.RhinoScriptProcessor.execute(RhinoScriptProcessor.java:195)
thanks in advance.
Using Alfresco Workflow API.
Note: wfDocs holds the array of doc nodes:
// 2 days from now
var dueDate2d = new Date((new Date()).getTime() + 2*(24*60*60*1000));
// Start workflow
var wfdef = workflow.getDefinitionByName("activiti$alfGroupReview");
if (wfdef) {
var wfparams = new Array();
wfparams["bpm:workflowDescription"] = "Please review";
wfparams["bpm:groupAssignee"] = people.getGroup( "GROUP_site_collaborators");
wfparams['bpm:workflowDueDate'] = dueDate2d;
wfparams['bpm:workflowPriority'] = 1;
wfparams['wf:notifyMe'] = true;
var wfpackage = workflow.createPackage();
for each (var n in wfDocs)
wfpackage.addNode(n);
var wfpath = wfdef.startWorkflow(wfpackage, wfparams);
var tasks = wfpath.getTasks();
for each (task in tasks)
task.endTask(null);
}
This code runs fine if:
docNode is not null. You should add a check for this.
Your group exists. Probably worth adding a check for this.
The workflow exists with the ID specified. Use the workflow console to confirm this. For example, the ID your provided is not an
out-of-the-box workflow. If it is custom, maybe you haven't deployed
the workflow successfully or you have the ID incorrect.
Also, do not use a variable called "workflow". Alfresco already defines a root-scoped object called "workflow". Speaking of that, feel free to use the workflow JavaScript API to invoke your workflow instead of an action. Either should work, though.
I ran your code successfully using the JavaScript console and a workflow id of "activiti$activitiParallelGroupReview" (and after changing your workflow variable to workflowAct).

Resources