I creating new workflow and I need to assign issues by condition.
For example:
During create issue if in the dropdown list I select "language_1" issue will be assigned to "translator_1" or if I select "langiage_2", issue will be assigned to "translator_2"
I tried to do this in workflow editor by creating post function, but this functions can't verify conditions. Does Jira have any other method to do it?
Use JIRA components to do this. Create a component named "language_1" with a component lead of your first user. When the issue is created set the component and leave the Assignee at automatic.
I used the Script runner plugin to do something similar by adding a post function to the required transaction. Code example:
from com.atlassian.jira import ComponentManager
customFieldManager = ComponentManager.getInstance().getCustomFieldManager()
userUtil = ComponentManager.getInstance().getUserUtil()
# read field
language = issue.getCustomFieldValue(customFieldManager.getCustomFieldObjectByName("Language"))
if (language == "language_1") {
issue.setAssignee(userUtil.getUserObject("translator_1"))
} else if (language == "language_2") {
issue.setAssignee(userUtil.getUserObject("translator_2"))
}
Related
I am developing a mod_plugin for Moodle and want to support the automatic activity completion with a custom rule. I followed the official documentation and implemented all necessary functions. In the lib.php [pluginname]_supports method I have registered, FEATURE_GRADE_HAS_GRADE, FEATURE_COMPLETION_TRACKS_VIEWS, FEATURE_COMPLETION_HAS_RULES.
The \mod_[pluginname]\completion\custom_completion class defines a custom rule named "completiontest" in get_defined_custom_rules(). During my tests I found out that the methods get_state(), get_sort_order() and get_custom_rule_descriptions() are never executed. Also I don't see any output via activity_information().
I have cleared all caches, created new instances of my activity module, with no result. My development environment uses Moodle 3.11.7 (Build: 20220509).
My custom_completion.php script:
<?php
declare(strict_types=1);
namespace mod_cwr\completion;
use core_completion\activity_custom_completion;
class custom_completion extends activity_custom_completion {
public function get_state(string $rule): int {
return COMPLETION_INCOMPLETE;
}
public static function get_defined_custom_rules(): array {
return [
'completiontest'
];
}
public function get_custom_rule_descriptions(): array {
return [
'completiontest' => 'testout'
];
}
public function get_sort_order(): array {
return [
'completionview',
'completiontest',
'completionusegrade'
];
}
}
Test at the view.php:
$completion = new completion_info($course);
$completion->set_module_viewed($coursemodule);
if($completion->is_enabled($coursemodule) == COMPLETION_TRACKING_AUTOMATIC){
$completion->update_state($coursemodule, COMPLETION_INCOMPLETE, $USER->id);
}
$completiondetails = \core_completion\cm_completion_details::get_instance($coursemodule, $USER->id);
$activitydates = \core\activity_dates::get_dates_for_module($coursemodule, $USER->id);
echo $OUTPUT->activity_information($coursemodule, $completiondetails, $activitydates);
At the mod_form.php I check with completion_rule_enabled() if a custom rule is activated by the settings.
Does anyone have any idea what the problem could be?
Looking at the mod_forum plugin code showed me, that the get_state($rule) method does not observe all custom rules, only those selected in the settings. How do I tell Moodle to use a specific custom rule?
You appear to be calling update_state() and passing in the possible state change as COMPLETION_INCOMPLETE.
This is a way of telling Moodle "if the state is already incomplete, don't bother doing any expensive completion calculations to check if it should change state".
If you want Moodle to check and then (potentially) change the state to "complete", then pass COMPLETION_COMPLETE. If you really don't know which way it could be switching, then leave the param at the default COMPLETION_UNKNOWN (a good example would be forum completion - if you have just created a forum post, then you might cause the forum to be marked as "complete", but you certainly can't cause the forum to be marked as "incomplete", so you can pass the COMPLETION_COMPLETE parameter, so Moodle knows it only needs to check for changes if the forum is not already "complete").
Also, don't bother passing $USER->id as the third parameter - that's the default which is used of you don't pass anything.
As for telling Moodle which rules to use - it is up to you, when your function is called, to check your plugins settings to determine which rules are in use (and any other relevant configuration - e.g. with mod_forum, it needs to check how many posts are required for completion).
Thank you for the support. Got it running.
I now use $completion->update_state($coursemodule, COMPLETION_COMPLETE); and also had to fix [pluginname]_get_coursemodule_info() with $result->customdata['customcompletionrules']['completiontest'] = $cwr->completiontest; and totally forgot the return $result;.
I am new to Katalon Studio and I am facing an issue regarding selection of the drop down.
Please find below the details:
This is the HTML :
I have tried using selectByIndex with the object xpath as:
//div[#class='paCriteriaContainer']//select[#class = 'pa-criteria-select a-select initialized']
It does not select any option and fails with an error stating "Unable to select option by index '2' of object"
Note:
I tried clicking on the input and then selecting the option, but that doesn't seem to work either.
Selecting by label and value don't work either
Please help me here.
Thank you
Try to capture an object and then use following methods :
WebUI.click(findTestObject(Your captured object))
WebUI.selectOptionByValue(findTestObject(Your captured object), 'TEST (2020)', false)
Did you done as I've described and it does not work ?
I tried clicking on the input and then selecting the option, but that doesn't seem to work either.
Are you sure you are clicking the right element in this case?
Try the following instead: create programmatically the element and select by value (note, value isn't the text contained, it is the value html attribute):
TestObject to = new TestObject().addProperty("xpath", ConditionType.EQUALS, "//div[#class='paCriteriaContainer']//select[#class = 'pa-criteria-select a-select initialized']")
WebUI.selectOptionByValue(to, '40696', false)
You have some options to do that, I reggardly suggest you that use always the xpath to reach all the elements that you want to use. The reasson is because the object reports usually fail and, in my opinion, this way is so much more complicated.
But obviously, the xpath will change if the web do, so take care with it.
The imports you need:
import static org.junit.Assert.*
import org.openqa.selenium.By
import org.openqa.selenium.Keys
import com.kms.katalon.core.webui.driver.DriverFactory as DriverFactory
def driver = DriverFactory.getWebDriver()
//If you want to click your input would be:
WebUI.click(WebUI.convertWebElementToTestObject(driver.findElement(By.xpath("(//input[#id='a-select-paCricteriaId_6908'])"))))
//**you just can click on "TestObject" type, and findElement returns "Element" type**
And if you want to select the option you need to know the whole path (I cannot get it with the given information).
An important tip for testing the xpath is to use this function in console mode (F12):
function getElementByXpath(path) {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
//And this code in the same console to test your xpath:
getElementByXpath("YOURTESTXPATH")
Furthermore, there are other ways to reach the same objetive with xpath, for example:
import com.kms.katalon.core.testobject.TestObject as TestObject
...
TestObject tobj = new TestObject('myTestObject')
String expr = '/some/valid/xpath/expression'
tobj.addProperty('xpath', ConditionType.EQUALS, expr)
WebUI.click(tobj)
You have a lot of information if you google "how to get elements by xpath katalon".
Here you can get official information about it:
https://docs.katalon.com/katalon-studio/tutorials/detect_elements_xpath.html#what-is-xpath
Test in the browser console
$x('//*[contains(#class, "pa-criteria-select a-select initialized")]')
if more than one result appear then you can access it like this
$x('(//*[contains(#class, "pa-criteria-select a-select initialized")])[1]')
then you can also access their children
$x('(//*[contains(#class, "pa-criteria-select a-select initialized")])[1]/option')
Use WebUI.selectOptionByIndex keyword but the object should point to the select tag instead of the div.
Update the object element and your code should work
I am trying to write a trigger that will copy a field on a custom object from one field to another during the save or when things have changed. So the custom object is McLabs_Property__c, the field I want to update is the standard field Name, I would like to update this name with the custom field McLabs_Property_Name__c
trigger <name> on McLabs2__Property__c (before insert, before update)
{
for (McLabs2__Property__c a:trigger.new)
{
a.name__r = a.McLabs2_Property_Name__c;
}
}
Thank you very much in advance.
You could also consider using a field update workflow rule.
TL;DR version:
In CQ workflows, is there a difference between what's available to the OR Split compared to the Process Step?
Is it possible to access the /history/ nodes of a workflow instance from within an OR Split?
How?!
The whole story:
I'm working on a workflow in CQ5 / AEM5.6.
In this workflow I have a custom dialog, which stores a couple of properties on the workflow instance.
The path to the property I'm having trouble with is: /workflow/instances/[this instance]/history/[workItem id]/workItem/metaData and I've called the property "reject-or-approve".
The dialog sets the property fine (via a dropdown that lets you set it to "reject" or "approve"), and I can access other properties on this node via a process step (in ecma script) using:
var actionReason;
var history = workflowSession.getHistory(workItem.getWorkflow());
// loop backwards through workItems
// and as soon as we find a Action Reason that is not empty
// store that as 'actionReason' and break.
for (var index = history.size() - 1; index >= 0; index--) {
var previous = history.get(index);
var tempActionReason = previous.getWorkItem().getMetaDataMap().get('action-message');
if ((tempActionReason != '')&&(tempActionReason != null)) {
actionReason = tempActionReason;
break;
}
}
The process step is not the problem though. Where I'm having trouble is when I try to do the same thing from inside an OR Split.
When I try the same workflowSession.getHistory(workItem.getWorkflow()) in an OR Split, it throws an error saying workItem is not defined.
I've tried storing this property on the payload instead (i.e. storing it under the page's jcr:content), and in that case the property does seem to be available to the OR Split, but my problems with that are:
This reject-or-approve property is only relevant to the current workflow instance, so storing it on the page's jcr:content doesn't really make sense. jcr:content properties will persist after the workflow is closed, and will be accessible to future workflow instances. I could work around this (i.e. don't let workflows do anything based on the property unless I'm sure this instance has written to the property already), but this doesn't feel right and is probably error-prone.
For some reason, when running through the custom dialog in my workflow, only the Admin user group seems to be able to write to the jcr:content property. When I use the dialog as any other user group (which I need to do for this workflow design), the dialog looks as though it's working, but never actually writes to the jcr:content property.
So for a couple of different reasons I'd rather keep this property local to the workflow instance instead of storing it on the page's jcr:content -- however, if anyone can think of a reason why my dialog isn't setting the property on the jcr:content when I use any group other than admin, that would give me a workaround even if it's not exactly the solution I'm looking for.
Thanks in advance if anyone can help! I know this is kind of obscure, but I've been stuck on it for ages.
a couple of days ago i ran into the same issue. The issue here is that you don't have the workItem object, because you don't really have an existing workItem. Imagine the following: you are going through the workflow, you got a couple of workItems, with means, either process step, either inbox item. When you are in an or split, you don't have existing workItems, you can ensure by visiting the /workItems node of the workflow instance. Your workaround seems to be the only way to go through this "issue".
I've solved it. It's not all that elegant looking, but it seems to be a pretty solid solution.
Here's some background:
Dialogs seem to reliably let you store properties either on:
the payload's jcr:content node (which wasn't practical for me, because the payload is locked during the workflow, and doesn't let non-admins write to its jcr:content)
the workItem/metaData for the current workflow step
However, Split steps don't have access to workItem. I found a fairly un-helpful confirmation of that here: http://blogs.adobe.com/dmcmahon/2013/03/26/cq5-failure-running-script-etcworkflowscriptscaworkitem-ecma-referenceerror-workitem-is-not-defined/
So basically the issue was, the Dialog step could store the property, but the OR Split couldn't access it.
My workaround was to add a Process step straight after the Dialog in my workflow. Process steps do have access to workItem, so they can read the property set by the Dialog. I never particularly wanted to store this data on the payload's jcr:content, so I looked for another location. It turns out the workflow metaData (at the top level of the workflow instance node, rather than workItem/metaData, which is inside the /history sub-node) is accessible to both the Process step and the OR Split. So, my Process step now reads the workItem's approveReject property (set by the Dialog), and then writes it to the workflow's metaData node. Then, the OR Split reads the property from its new location, and does its magic.
The way you access the workflow metaData from the Process step and the OR Split is not consistent, but you can get there from both.
Here's some code: (complete with comments. You're welcome)
In the dialog where you choose to approve or reject, the name of the field is set to rejectApprove. There's no ./ or anything before it. This tells it to store the property on the workItem/metaData node for the current workflow step under /history/.
Straight after the dialog, a Process step runs this:
var rejectApprove;
var history = workflowSession.getHistory(workItem.getWorkflow());
// loop backwards through workItems
// and as soon as we find a rejectApprove that is not empty
// store that as 'rejectApprove' and break.
for (var index = history.size() - 1; index >= 0; index--) {
var previous = history.get(index);
var tempRejectApprove = previous.getWorkItem().getMetaDataMap().get('rejectApprove');
if ((tempRejectApprove != '')&&(tempRejectApprove != null)) {
rejectApprove = tempRejectApprove;
break;
}
}
// steps up from the workflow step into the workflow metaData,
// and stores the rejectApprove property there
// (where it can be accessed by an OR Split)
workItem.getWorkflowData().getMetaData().put('rejectApprove', rejectApprove);
Then after the Process step, the OR Split has the following in its tabs:
function check() {
var match = 'approve';
if (workflowData.getMetaData().get('rejectApprove') == match) {
return true;
} else {
return false;
}
}
Note: use this for the tab for the "approve" path, then copy it and replace var match = 'approve' with var match = 'reject'
So the key here is that from a Process step:
workItem.getWorkflowData().getMetaData().put('rejectApprove', rejectApprove);
writes to the same property that:
workflowData.getMetaData().get('rejectApprove') reads from when you execute it in an OR Split.
To suit our business requirements, there's more to the workflow I've implemented than just this, but the method above seems to be a pretty reliable way to get values that are entered in a dialog, and access them from within an OR Split.
It seems pretty silly that the OR Split can't access the workItem directly, and I'd be interested to know if there's a less roundabout way of doing this, but for now this has solved my problem.
I really hope someone else has this same problem, and finds this useful, because it took me waaay to long to figure out, to only apply it once!
I have a site with a custom content type Content, which initially had a single workflow attached, content_workflow. There are several thousand existing instances of Content.
I now have a need to add a second workflow to this type, content_beta_workflow. How can I update all existing content to be part of the new workflow?
On a related note: if I want rename the initial workflow to content_alpha_workflow, how can I update all existing content to reflect this change?
If you are simply changing from one workflow to the other, follow these steps:
Go to Site Setup > Types
Select your custom content type from the drop down menu, the page will update to display the current workflow
Select your new workflow from the dropdown, a map will be generated showing each state in the current workflow
For each state, select the state in your new workflow that most closely matches (or is most appropriate)
When you save, all objects of your custom site will be updated to use the new workflow. For each state in the map from the original workflow, existing content in that state will be put into the state you chose in step 4 above. Security settings will be re-indexed and you are done.
As for renaming the old workflow, you can do so in the portal_workflow tool in the ZMI. But only change the human-facing Title of the workflow. Changing the ID may have side effects for the workflow history of your content.
edited
Okay, I see from your comment that you are looking to add a new workflow to a type in addition to the one it already has. Here's a bit of sample code to accomplish that:
my_type = 'Content' # This is your content portal_type name
my_wf = 'content_workflow_beta'
wf_chain = list(wf_tool.getChainForPortalType(my_type))
if my_wf not in wf_chain:
wf_chain.append(my_wf)
wf_tool.setChainForPortalTypes([my_type], wf_chain)
You can add this code in an upgrade step for the package that defines your content type and workflows. Add a call to updateRoleMappings on the workflow tool and you'll be set to use the new workflow through the standard Plone UI in addition to your original workflow.
As you've already found, you can also manually update the workflow history of all objects to rename workflow ID, but that's a pretty invasive step.
As workflow_history is a dict property on each content item, it was a case of adding or updating suitable items as required. First, I copied the GenericSetup for content_workflow to content_alpha_workflow. Next, I created content_beta_workflow and added it to the profile. Then I wrote the following upgrade step:
import logging
from DateTime import DateTime
def modify_content_workflow_history(context, logger=None):
if logger is None: logger = logging.getLogger('my.product')
# import the new workflows
context.portal_setup.runImportStepFromProfile('profile-my.product:default', 'workflow')
# set up some defaults for the new records
_history_defaults = dict(
action = None,
actor = 'admin',
comments = 'automatically created by update v2',
time = DateTime(),
)
_alpha_defaults = dict(review_state = 'alpha_state_1', **_history_defaults)
_beta_defaults = dict(review_state = 'beta_state_1', **_history_defaults)
for parent in context.parents.values():
for content in parent.content.values():
# don't acquire the parent's history
if 'parent_workflow' in content.workflow_history:
content.workflow_history = {}
# copy content_workflow to content_alpha_workflow
if 'content_workflow' in content.workflow_history:
alpha_defaults = context.workflow_history['content_workflow']
del content.workflow_history['content_workflow']
else:
alpha_defaults = (_alpha_defaults,) # must be a tuple
content.workflow_history['ctcc_content_alpha_workflow'] = alpha_defaults
# create the beta workflow with a modified actor
beta_defaults = dict(**_beta_defaults)
beta_defaults['actor'] = u'%suser' % parent.id
content.workflow_history['ctcc_content_beta_workflow'] = (beta_defaults,)
logger.info('Content workflow history updated')