Automatic activity not performing - tridion

Created a workflow with basic as below.
Created a calss library, used ProgId, set comvisible true and registerd the assembly in the Tridion server.
This is the way i have tested:
Created a component
Finished the activity from the work list.
Navigated to the "Global Work list" and finished the Reviewer activity by myself by choosing the "Back to Author" step and clicked the "Finish" button.
The item is not moved to the author. but when i finish the activity again from the global work list, the item moved to author.
It seems that my code is not performing the activity because i tried removed the below VB script code and tried with the default automatic script code.
' Script for Automatic Activity Content Manager Workflow
FinishActivity "Automatic Activity Finished"
It behaves the same as above. so i decided my code is not worked. Can any one please help on this?
Below is the VBScript I used in the script box of "Back to Author":
Option Explicit
Dim workflowHandler
Set workflowHandler = CreateObject("CoreComponentWorkflow.WorkflowHandler");
If Not workflowHandler Is Nothing Then
Call workflowHandler.MoveBackToActivity(Cstr(CurrentWorkItem.ID, "Create or Edit Component")
End If
Set workflowHandler = Nothing
Below is the C# Code:
public void MoveBackToActivity(string workitemid, string strActivitytoMove)
{
try
{
Session session = new Session();
WorkItem workitem = new WorkItem(new TcmUri("workitemid"), session);
ActivityInstance currentactivity = workitem.Activity as ActivityInstance;
ProcessInstance procInstance = currentactivity.Process as ProcessInstance;
IEnumerable<ActivityInstance> ieActivities = procInstance.Activities
.Select (s => s)
.Where (w => w.Title.IndexOf(strActivitytoMove) !=-1)
.OrderByDescending(w =>w.StartDate);
if (ieActivities != null && ieActivities.Count<ActivityInstance>() > 0)
{
ActivityInstance targetactivity = ieActivities.ElementAt(0);
User lastperformuser = targetactivity.Performers.ElementAt(targetactivity.Performers.Count<User>() - 1);
ActivityFinish finish = new ActivityFinish(targetactivity.FinishMessage, lastperformuser, workitem.Session);
currentactivity.Finish(finish);
}
}
catch (Exception ex)
{
throw ex;
}
}

Be aware that you are using an API that is NOT supported in Automatic Activities. The only processes where you are allowed to use TOM.NET are Event System handlers and Template Building Blocks as documented here.
Automatic Workflow Activities - if not developed with VBScript - must use the CoreService interface.
The good news is that I know for a fact this works - plenty of people got it to work in many implementations. The bad news (for you) is that the error is in your code. Have you tried debugging/step-by-step through your code yet? You can attach to to the workflow process (cm_wf_svc.exe) and figure out what's wrong with the code much faster than we can.
Here's a really simple snippet to finish an activity with CoreService:
ActivityFinishData activityFinish = new ActivityFinishData
{
Message = "Automatically Finished from Expiration Workflow Extension"
};
ActivityInstanceData activityInstance =
(ActivityInstanceData)processInstance.Activities[0];
client.FinishActivity(activityInstance.Id, activityFinish, readOptions);
BTW - If you intended to use TOM.NET anyway, why did you bother asking which API to use?

Following the Nuno's answer, yes you should change the code to use TOM or Core Services. TOM .Net is not supported because it is using a different thread apartment than the underlying technology we use for workflow (COM).
About the issue I have checked that you are calling the activity like this.
Call workflowHandler.MoveBackToActivity(Cstr(CurrentWorkItem.ID, "Create or Edit Component")
It looks like the activity name is not matching. there are some strange characters between "Edit" and "Component"
I hope this helps.

Automatic activities are executed by the Workflow agent service. An Assigned state may indicate that it's just not being picked up by the service. Is your service running correctly, and are things like queue notifications set up properly?

Related

What I misunderstood about the Mediator Pattern?

I'm new here and a beginner architect. I'm helping the company I work to design our new product and since I start it, I read a lot about but is never enough.
I decided to use Mediator Pattern (with Mediatr) to call my application layer.
It's cool and I got how to work with it sometimes, but sometimes I get confused.
For example, when we publish a document on our new product, we uses a RequestHandler to do everything and check all rules it needs, it's fine and works like a charm, but, when I want just a quick data, it looks likes too much for just a simple thing.
As an example, every time the user do any kind of action on my web application, I have to check if he is still logged. We have single login per user, so, if the same user connect anywhere else, the older session expires. We do it by saving on database.
On every action of my app, I go to base and check if the session key is the same, as bellow.
var sessionKey = bibliotecaCookie.Value;
var mediator = controller.GetMediator();
var isUserSessionKeyValidRequest = new IsUserSessionKeyValidRequest()
{
sessionKey = sessionKey
};
var isValidSession = mediator.Send(isUserSessionKeyValidRequest).Result;
if (!isValidSession)
throw new UnauthorizedAccessException();
So, I have a RequestHandler (a Handler and a Request which returns a bool) just to check if the user session is ok.
When the handler catches this request, it goes to database and execute a simple rule which is "Is the passed session the same as the stored session key?".
Is it right? Is it the right approach? Did I understand it right?
Thanks in advance guys

Partial Realms - Why and When are They Created?

I am using Realm and building a Swift mobile app. I am really struggling to understand why and when Partial realms are created.
Here is my scenario:
a user logs in to the app and is brought to the first view controller.
In the first view controller in view did load, I am executing a query to get the current user, subscribing to the query and adding an observer to let me know when the data is synced:
let currentUserArr = realm.objects(DBUser.self).filter("id == %#", userId)
self.subscription = currentUserArr.subscribe(named: "current user")
self.subscriptionToken = self.subscription.observe(\.state, options: .initial) { state in
switch state {
case .creating:
print("creating")
case .pending:
print("pending")
case .complete:
print("complete")
self.artist = currentUserArr[0]
case .invalidated:
print("invalidated")
case .error(let err):
//seal.reject(err)
print(err)
}
}
This makes sense that if I check Realm Cloud, I have a new partial realm created with path as:
/db/__partial/DyeOy3OR4sNsqMi2OmDQQEzUa8F3/~7f11cf52
However, here is where my confusion starts. I log the user out. I log back in and again the code above executes. My thought would be that Realm would just reuse the partial already created, but instead it creates an entirely new partial.
/db/__partial/DyeOy3OR4sNsqMi2OmDQQEzUa8F3/~8bc7bc49
Is this by design or should I somehow be reusing partials rather than having a new one created every time a query is executed (even if it is executed by the same user)?
I have posted on Realm Forums as well:
https://forums.realm.io/t/realm-platform-realm-path-partial-s/2833
I don't believe I was actually logging the current sync user out. Upon further testing, once I did log out and log back in, the existing partial was re-used. This is a non-issue.

Flex: recover from a corrupt local SharedObject

My Flex app uses local SharedObjects. There have been incidents of the Flash cookie getting corrupt, for example, due to a plugin crash. In this case SharedObjects.getLocal will throw an exception (#2006).
My client wants the app to recover gracefully: if the cookie is corrupt, I should replace it with an empty one.
The problem is, if SharedObject.getLocal doesn't return an instance of SharedObject, I've nothing to call clear() on.
How can I delete or replace such a cookie?
Many thanks!
EDIT:
There isn't much code to show - I access the local cookie, and I can easily catch the exception. But how can I create a fresh shared object at the same location once I caught the exception?
try {
localStorage = SharedObject.getLocal("heywoodsApp");
} catch (err:Error) {
// what do I do here?
}
The error is easily reproduced by damaging the binary content of a Flash cookie with an editor.
I'm not really sure why you'd be getting a range error - esp if you report that can find it. My only guess for something like this is there is a possibility of crossing boundries with respect to the cross-domain policy. Assuming IT has control over where the server is hosted, if the sub-domain ever changed or even access type (from standard to https) this can cause issues especially if the application is ongoing (having been through several releases). I would find it rather hard to believe that you are trying to retrieve a named SO that has already been named by another application - essentially a name collision. In this regard many of us still uses the reverse-dns style naming convention even on these things.
If you can catch the error it should be relatively trivial to recover from: - just declare the variable outside the scope of the try so it's accessible to catch as well. [edit]: Since it's a static method, you may need to create a postfix to essentially start over with a new identifier.
var mySO:SharedObject;
....
catch(e:Error)
{
mySO = SharedObject.getLocal('my.reversedns.so_name_temp_name');
//might want to dispatch an error event or rethrow a specific exception
//to alert the user their "preferences" were reset.
}
You need to be testing for the length of SharedObject and recreate if it's 0. Also, always use flush to write to the object. Here's a function we use to count the number of times our software is launched:
private function usageNumber():void {
usage = SharedObject.getLocal("usage");
if (usage.size > 0) {
var usageStr:String = usage.data.usage;
var usageNum:Number = parseInt(usageStr);
usageNum = usageNum + 1;
usageStr = usageNum.toString();
usage.data.usage = usageStr;
usage.flush();
countService.send();
} else {
usage.data.usage = "1";
usage.flush();
countService.send();
}
}
It's important to note that if the object isn't available it will automatically be recreated. That's the confusing part about SharedObjects.
All we're doing is declaring the variable globally:
public var usage:SharedObject;
And then calling it in the init() function:
usage = SharedObject.getLocal("usage");
If it's not present, then it gets created.

SynchronizationContext and Bookmarks in WF4

I am running workflows under asp.net and using SynchronizationContext to make the page "wait" for the workflow. Here is how I run the workflow instance under asp.net:
var workflowApplication = new WorkflowApplication(activity);
SynchronizationContext syncContext = SynchronizationContext.Current;
workflowApplication.Completed = delegate { syncContext.OperationCompleted(); };
workflowApplication.SynchronizationContext = syncContext;
syncContext.OperationStarted();
workflowApplication.Run();
In one of the activities I use a bookmark. Now I want the page processing to continue whenever I call CreateBookmark. I tried calling SynchronizationContext.Current.OperationCompleted() before setting the bookmark but that crushes asp.net site when the workflow resumes and completes (I think the workflow instance calls OperationCompleted again when it completes and the error raises)
How can I work with bookmarks under Asp.Net, any ideas?
The Completed Property isn't being called when you persist the workflow with a bookmark, try adding this line after you've set up the completed property:
workflowApplication.PersistableIdle = args => {
syncContext.OperationCompleted();
return PersistableIdleAction.None;
};
This should then be called instead of completed, and the ASP.NET code should regain control. Change the PersistableIdleAction enum to whatever is required, I've just picked None for example code.

Quartz.NET trigger not firing

i am using Quartz.NET in my ASP.NET web application. i put the following code in a button click handler to make sure that it executes (for testing purposes):
Quartz.ISchedulerFactory factory = new Quartz.Impl.StdSchedulerFactory();
Quartz.IScheduler scheduler = factory.GetScheduler();
Quartz.JobDetail job = new Quartz.JobDetail("job", null, typeof(BackupJob));
Quartz.Trigger trigger = Quartz.TriggerUtils.MakeDailyTrigger(8, 30); // i edit this each time before compilation (for testing purposes)
trigger.StartTimeUtc = Quartz.TriggerUtils.GetEvenSecondDate(DateTime.UtcNow);
trigger.Name = "trigger";
scheduler.ScheduleJob(job, trigger);
scheduler.Start();
here's "BackupJob":
public class BackupJob : IJob
{
public BackupJob()
{
}
public void Execute(JobExecutionContext context)
{
NSG.BackupJobStart();
}
}
my question: why is "BackupJobStart()" not firing? i've used similar code before and it worked fine.
EDIT: #Andy White, i would have it in Application_Start in Global.asax. this doesn't work which is why i moved it to a button click handler to narrow down the problem.
Do you have the Quartz.NET logging hooked up? I once had a problem with a job not executing (I forget why), but once I got the Quartz.NET logging going, the problem was obvious.
It's worth a try (if you're not already doing it):
https://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html
http://netcommon.sourceforge.net/
http://netcommon.sourceforge.net/documentation.html
Update: Simply add this to your program.cs to enable console logging:
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter { Level = Common.Logging.LogLevel.Info};
Maybe it's a problem of time.
I've had the same problem as you, and I live in a country which time is UTC + 2. So, when I set the StartTimeUtc to the trigger, I used DateTime.Now, so the trigger didn't have to fire until two hours later, and I thought it has to be fired in the very moment my code started.
Look carefully the time of the trigger's execution and its StartTime
Another possibility is the way you're running the scheduler. I'm not totally sure, but you may run into problems trying to run a scheduling threads in an ASP.NET application. Putting the SchedulerFactory/Scheduler objects in a button click handler doesn't seem like it would give you the desired results.
You may need to create the scheduler at a more "global" level, so that it can run in the "background" of the application. It might also make sense to move any scheduled work into a separate windows service, so that you don't have to maintain the scheduler in the web app.
When you had success in the past, how were you invoking the scheduler?
In my case, there was an issue with IoC - there were some Interfaces that weren't implemented. I could see what was wrong with mine by adding logging:
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter { Level = Common.Logging.LogLevel.Info};
to Program.cs as suggested by Andy White

Resources