Progressing Alfresco workflows through web script - workflow

I have an Alfresco document reference; what I'm looking for is a way to access workflow attached to that document and finish it (or progress it to the next transition) through Javascript.
Almost every example on the web shows how to start workflow, and from the dashlet I could call task command processor (/alfresco/command/task/end/[/transition]) if I knew the task ID, but how do I do the same thing from server-side web script starting only from the document reference?
There must be a way to access workflows from document and manage them programatically.

From a document nodeRef you can signal the current task like this:
var docNodeRef = "workspace://SpacesStore/<GUID HERE>";
var transitionId = "some action";
var theDocument = search.findNode(docNodeRef);
foreach (currWorkflow in theDocument.activeWorkflows)
{
var path = currWorkflow.paths[currWorkflow.paths.length-1];
var task = path.tasks[0];
for (var transitionKey in task.transitions)
{
if (task.transitions[transitionKey] == transitionId)
{
path.signal(transitionId);
break;
}
}
}
If you want to signal the default transition you can skip the inner loop and just do this:
var docNodeRef = "workspace://SpacesStore/<GUID HERE>";
var transitionId = "some action";
var theDocument = search.findNode(docNodeRef);
foreach (currWorkflow in theDocument.activeWorkflows)
{
var path = currWorkflow.paths[currWorkflow.paths.length-1];
var task = path.tasks[0];
// Signal default transition
path.signal(null);
}

Well, I still don't know how to transition, but there are a couple of things I found out.
First, I can access workflows document participates in and cancel it:
for each (workflow in document.activeWorkflows) {
workflow.cancel();
}
However, I'm still not quite sure how to progress tasks. I can get to the task and do something with it:
var task = workflow.getTask(taskId);
task.endTask(transitionId);
...but I still have no idea how to get to taskId or transitionId, either programmatically or through Alfresco.
EDIT: figured it out, transitionId is actually transition name as defined in workflow processdefinition XML:
<transition name="SomeTransitionId" to="end">
Also, to get list of tasks from the workflow you can iterate through paths (workflow.getPaths()) and then through tasks with path.getTasks().

Related

Service Now REST API script for listening from DevOps web hooks

I am wondering how connect between DevOps Boards Incidents and Tasks (web hooks used) to ServiceNow. The requirement is Service Now will need to listen every time there is an update to Azure DevOps boards incidents or Tasks.
I have created a Scripted REST API at Service Now with the following Example script:
`(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {
var body = request.body.data;
var eventType = request.headers['auth_token'];
var workItem = body.resource;
var title = workItem.fields['System.Title'];
var description = workItem.fields['System.Description'];
var state = workItem.fields['System.State'];
var priority = workItem.fields['System.priority'];
if(eventType == 'workitem.created') {
title = workItem.fields['System.Title'];
description = workItem.fields['System.Description'];
state = workItem.fields['System.State'];
} else if(eventType == 'workitem.updated') {
title = workItem.fields['System.Title'];
description = workItem.fields['System.Description'];
state = workItem.fields['System.State'];
}
response.setStatus(200);
})(request, response);`
Now, Just wondering how to invoke this API end point when there is an update.
If you are on the Scripted REST Resource form in ServiceNow, then you should see a "Explore REST API" link at the bottom of the page. If you click that link it will take you page that you can use to test your API. Right below the "Send" button there should be links to generate code samples to call your API.

How to reduce response size of Moodle's "cron" page

I'm to setting up web-based cron jobs through cron-jobs.org to my site with Moodle installation. When I do the cron job through command line way, /usr/bin/php path/to/moodle/admin/cli/cron.php, I don't have trouble with execution and results; but in cron-jobs status of my cron task appear like "Response too big".
What can I do?
Alternatively, you can use Google Apps scripts: https://developers.google.com/apps-script
1 - Create a Google SpreadSheet
2 - Click Tools > Script Editor
3 - Copy and paste code below: (replace url)
function runCron() {
var url = "http://yourmoodle.com.br/moodle/admin/cron.php";
var options = {
'method': 'get',
//muteHttpExceptions: true,
};
var maxRows= 1440;
try{
var response = UrlFetchApp.fetch(url, options).getContentText().substring(0,50000);
Logger.log(response);
} catch (e) {
Logger.log(e);
var response = e;
}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// This logs the value in the very last cell of this sheet
sheet.insertRowAfter(1);
sheet.getRange('A2').setValue(new Date()).setNumberFormat("MM/dd/yyyy hh:mm");
sheet.getRange('B2').setValue(response);
var lastRow = sheet.getLastRow();
if(lastRow>maxRows){
sheet.deleteRow(lastRow);
}
}
4 - Save and edit your project's name
5 - Click Edit > Current project's trigger
6 - Add a new trigger
7 - Set event source to Time-driven and type of time based trigger to Minutes timer
8 - Save and sign in to grant permission for your project.
If you have a choice, do not use the web based cron. It is likely to be removed in a future Moodle version.
...it is recommended to only run the cron from the command line or set a cron password for remote access.
Reference:
https://docs.moodle.org/38/en/Cron#The_web_based_Moodle_cron_command
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app
https://developers.google.com/apps-script/guides/sheets
The solution is to find another web cron provider which does not have the limitations cron-job.org does.
See this one: https://www.easycron.com/
It's still free but can handle larger amount of data being returned.

ASP.NET Core, where is FindByID?

I have an ASP.NET Core 2.0 application and I'm trying to attach a user to a model:
var user = _userManager.FindByIdAsync(Model.Author);
var promotion = new Promotion()
{
Title = Model.Title,
User = user //error here,
Created = DateTime.Now
};
The problem with this code is that I can't assign user to promotion.User as user is the result of an async operation. I'd prefer not to use FindByIdAsync but for some reason I can't find FindById.
UserManager contains only async API and FindByIdAsync actually returns Task<User> instead of User. So you need to make your code async also and use FindByIdAsync like this:
var user = await _userManager.FindByIdAsync(Model.Author); // will return the User
Only if it is not possible leave your code synchronous, e.g. by calling Result property of the Task which will cause your thread to block until the result is available
var user = _userManager.FindByIdAsync(Model.Author).Result;

Why doesn't Deletion in Parse-server require to obtain that object beforehand?

I came across a strange behaviour. Even though my unit tests are passing, I don;t quite understand this.
func testDeleteMoment() {
// Create a Moment for current user
let expectCreate = expectation(description: "create moment should succeed")
Datastore.shared.createMoment(notes: "myNotes", rating: 1, time: Date(), location: "", isPublic: true, completion: {(success, error) in
XCTAssertTrue(success)
expectCreate.fulfill()
})
waitForExpectations(timeout: 5) { (error) in
XCTAssertNil(error, "Test timed out. \(String(describing: error?.localizedDescription))")
}
let query = PFQuery(className: "Moment")
var objectId = ""
do {
query.whereKey("owner", equalTo:PFUser.current()!)
let object = try query.getFirstObject()
objectId = object.objectId!
} catch {}
let task = Datastore.shared.deleteMoment(id: objectId)
task.waitUntilFinished()
let query2 = PFQuery(className: "Moment")
query2.whereKey("owner", equalTo:PFUser.current()!)
let task3 = query2.countObjectsInBackground()
task3.waitUntilFinished()
XCTAssertEqual(task3.result, 0)
}
While writing my datastore.deleteMoment(), I noticed that unlike saveEventually(), deleteEventually() doesn't have a completion handler. Instead, it only comes with a BFTask<NSNumber>. Hence I experimented with the following code and the unit test passes to my surprise.
func deleteMoment(id: String) -> BFTask<NSNumber> {
let pfMoment = PFObject(className:"Moment")
pfMoment.objectId = id
return pfMoment.deleteEventually()
}
How comes that I don't have to retrieve the object before hand in order to delete it? Why isn't there then just a method to delete the object via an id, instead of doing it like this?
How comes that I don't have to retrieve the object before hand in order to delete it?
Parse lets you work with "shell objects". Basically, if you know the object id you can attach it to a blank instance of a Parse Object subclass and do some actions on it. You can create a shell, assign a couple values, and call object.save() and it will update just those fields. You can create a blank object, assign an id, then call object.fetch() to obtain it, without having to have gotten the object from a query or pointer on another object.
The reason it works is because in order to perform the database operations, the object id and class name are the only pieces of information required. If you just want to update a couple fields, you don't need to pull all of the rest of the data to do so. And if you want to destroy an object, why would you need all of it's data locally first? You just need to remove the entry in the database with the matching _id.
Why isn't there then just a method to delete the object via an id, instead of doing it like this?
Nobody has built it yet. Parse is open source, and while it's a pretty phenomenal resource today, there's certainly a lot of room for improvement. Feel free to add this feature and create a PR. Though you could easily build your own wrapper by extending the Parse.Object class with this function, that basically does what you already did under the hood.

Create a job in Parse which delete all old objects in all Classes

I have created a chat app in IOS with Parse.com. I want after 30min to delete all old messages. Every chatroom is a class in parse which contains the messages between two users.
Can you help me to create a repeatly job in parse that search for old messages in all classes and deletes them ?
To avoid exceeding the request limit, maybe you should delete the objects in every single minute. You can find out all the objects and delete them by something like this.
var time = new Date(new Date() - 30*60*1000); // 30min ago
var query = new Parse.Query('ChatLog');
query.lessThan('createdAt', time);
query.limit(1000);
query.find().then(function(results) {
return Parse.Object.destroyAll(results);
});
The answer from #iForests is good. There is likely a way to do this same thing with .each() which won't have any query limits. Maybe something like:
var thirtyMinutes = 30*60*1000
var now = new Date()
var thirtyMinutesAgo = new Date(now-thirtyMinutes)
var query = new Parse.Query('ChatLog')
query.lessThan('createdAt', thirtyMinutesAgo)
var objectsToDelete = []
query.each(function(object){
objectsToDelete.push(object)
}).then(function(){
var howManyDeleted = objectsToDelete.length
Parse.Object.destroyAll(objectsToDelete)
status.success(howManyDeleted+' chat logs deleted.')
}, function(error){
status.error('Error during background job.')
})
Forgive my lack of semi-colons--I'm a Swift person :) Good luck!