CA Service Management

 View Only

SPEL: Register custom webengine OP 

Jun 11, 2015 09:33 AM

Hello,

I wish to resolve how to use custom webengine operations,

actually I make it work but there is still a lot of question,

so I ask community for assistance.

 

 

    How Webengine OPs are used?

 

Webengine can perform various operations like SPEL execute directly from URL without triggering any activities.

Most common operations are:

    - SEARCH

    - UPDATE

    - SHOW_DETAIL

    - DISPLAY_FORM

but there is a lot of "special" operations, all of them could be found in "bopcfg\www", within op_*.cfg files.

 

Basic infromation could be found in op_table.cfg file.

 

 

    How to make custom operation?


    1. Register new Webengine OP:

Create new file and call it op_custom.cfg, locate it in site/mods/www folder.

Add the followed text to recently created file:

#    Z_TEST - new OP; (Uppercase)
#    z_test - SPEL function;
#    MODIFY - factory access type;
#    UPDATE - type of request;
Z_TEST z_test MODIFY UPDATE























Reminder: SPL file with used functions should be located in the same folder as .cfg file.

 

    2. Create link to call your OP

I've used detail_cr form to call my function,

here is code to make button with test function:

var url = cfgCgi +
"?SID=" + cfgSID +
"+FID=" + fid_generator() +
"+OP=Z_TEST" +
"+FACTORY=cr" +
"+PERSID=" + argPersistentID +
"+TEST_VAL=123";
<PDM_MACRO name=button Caption="TEST OP" Func="display_new_page(url, ahdframeset.workframe);" hotkey_name="TEST_OP[P]" ID=TEST_OP>



















Note: I've called my function to background (workframe) page.

 

    3. Create SPL function

Create new file and call it z_test.spl, locate it in site/mods/www (as .cfg file) folder.

Here test code:

z_test(...)
{
  logf(SIGNIFICANT, "argc = '%d'", argc);
  logf(SIGNIFICANT, "event = %s", event());
  int arg0;
  object arg1;
  arg0 = argv[0];
  arg1 = argv[1];
  dump_args(arg1); //
  upd_val(arg1.FACTORY, format("persistent_id = '%s'", arg1.PERSID), 1, 1, "summary", arg1.TEST_VAL);
}


















Note: I've used my upd_val function from this document: SPEL: Object update.

Regular SPEL installation tip:

    a. Copy SPL file from majic folder to folder where your op_*.cfg file is located;

    b. Remove factory (ex: marco:: ) from function name.

 

    4. Useful tips:

- spl and cfg files are not affected by spel_srvr proccess, so you can debug them by killing webengine (pdm_kill webengine);

- use pdm_logstat -n web:locat 1000 to see what dump_args(arg1) returns;

- OP functions returns object but it was NULL in all of my tries.

 

 

    CALLBACK function

 

spel:

z_test(...)
{
  int some_id; // You should pass the same value to next functions
  some_id = argv[0]; // here it is
  object new_obj; // Received object
  new_obj = argv[1];
  new_obj.CALLBACK="alert(myMsg)";
  string new_js; // our callback js code
  new_js = "var myMsg='Hello World!';";
  logf(SIGNIFICANT, "PERSID = %s", new_obj.PERSID);
  logf(SIGNIFICANT, "CALLBACK = %s", new_obj.CALLBACK);
  logf(SIGNIFICANT, "HTMPL = %s", new_obj.HTMPL);
  logf(SIGNIFICANT, format_to_js(new_js)); // You can use format_to_js function to unescape quotes
  send_frame_resp((long)some_id, (object)new_obj, (string)new_js);
}








 

htmpl:

var url = cfgCgi +
"?SID=" + cfgSID +
"+FID=" + fid_generator() +
"+OP=Z_TEST" +
"+FACTORY=cr" +
"+PERSID=" + argPersistentID +
"+POPUP_NAME=" + '$args.KEEP.POPUP_NAME' +
"+KEEP.POPUP_NAME=" + '$args.KEEP.POPUP_NAME' +
"+CALLBACK=test";
<PDM_MACRO name=button Caption="TEST OP" Func="display_new_page(url, ahdframeset.workframe);" hotkey_name="TEST_OP[P]" ID=TEST_OP>








 

Result:

this code loads to workframe frame:

<html><head><script language="JavaScript" src="/CAisd/scripts/window_manager.js"></script><style type="text/css"></style>
<script>
var myMsg='Hello World!';var ahdtop = get_ahdtop(true);
if ( typeof ahdtop == 'object' ) {
  var win = ahdtop.AHD_Windows['USD1435149643020'];
  if ( typeof win == 'object' )  win.alert(myMsg);
}
</script>


</head><body marginwidth="0" marginheight="0"></body></html>







 

 

    What is still unresolved


Each WebEngine Operation manages 2 arguments:

argv[0] - long integer, such as session id or something like it, which should be passed to the next function, if you call it.

argv[1] - object which contains all attributes passed via link, (can be dumped by dump_args(argv[1])).

So If you need to call another method you shall pass 2 args to it, argv[0] that you already got and object with new attributes.

I can't find a way how to create new object, this is my main question.

Sure you can predefine all of attributes in first object and then update them but this causes excess data.

 

 

     Useful examples


Custom WebEngine OP : Who is currently watching object

Statistics
0 Favorited
146 Views
0 Files
0 Shares
0 Downloads

Tags and Keywords

Comments

May 09, 2023 09:41 AM

hi *,

this is how we call spel from gui:

new operation in site/mods/www/op_add.cfg:
CALL_SPEL call_spel NONE READ NOLOGIN

spel method for the webengine in site/mods/www/op_add.spl:
void call_spel(int cid, object request) {
    
    string mth; mth = "call_spel()";
    logf(TRACE, "%s: start.", mth);
    
    int i;
    string response;    
    
    send_wait(0, request, "call_attr", "METHOD", "get_val");
    send_wait(0, top_object(), "call_attr", "api", msg[0], request);
    
    response = '["' + (string)msg_error() + '"';
    for(i = 0; i < msg_length(); i++) {
        response += ', "' + msg[i] + '"';
    }
    response += ']';
    
    logf(TRACE, "%s: cid = %s, response = %s", mth, cid, response);
    send(this, "html_text", cid, response);
    
    logf(TRACE, "%s: end.", mth);
}

new spel method for the spel_srvr in site/mods/majic/call_spel.mod:
OBJECT api {
   FACTORY {
      METHODS {
               call_spel_example(...);
        };
    };
};

spel code for spel_srvr in site/mods/majic/call_spel.spl:
void api::call_spel_example(...) {
    
    string mth; mth = "api::call_spel_example()";
    logf(TRACE, "%s: start.", mth);
    
    object request;
    int len;    
    
    len = 1;
    request = argv[0];
    
    while(1) {
        send_wait(0, request, "call_attr", format("ARG%d", len), "get_val");
        if(is_empty(msg[0])) {
            break;
        }
        else {
            logf(TRACE, "%s: ARG%d = '%s'", mth, len, msg[0]);
            set_return_data(msg[0]);
            len += 1;
        }
    }
    
    set_error(0);
    
    // clear_return_data();
    // set_return_data("ups");
    // set_error(1);
    
    logf(TRACE, "%s: end.", mth);
}

javscript function for sending request to webengine in site/mods/www/wwwroot/scripts/sidemods.js:
function call_spel() {
    
    var url = cfgCgi + "?SID=" + cfgSID + "+FID=" + fid_generator() + "+OP=CALL_SPEL+METHOD=" + arguments[1];
    for(var i = 2; i < arguments.length; i++) {
        url += "+ARG" + (i -1) + "=" + arguments[i];
    }
    AsyncAjaxCall(url, eval(arguments[0]), "GET");
}

javscript callback function to get response in htmpl form:

function call_spel_cb(response) {

    var msg = JSON.parse(response.response.replace(/.$$/,''));
    
    if(msg[0] == "1") {
        alert("Error = " + msg[1]);
    }
    else {
        alert("Response = " + msg[1] + ", " + msg[2]);
    }
}



trigger to invoke to stuff in htmpl form:
<PDM_MACRO NAME=button id=zcall_spel caption="Call my spel stuff" hotkey_name="Call my spel stuff" Tooltip="Call my spel stuff"  func="call_spel('call_spel_cb', 'call_spel_example', 'test_arg1', 'test_arg2')">

with this, you can invoke a spel method from everywhere in gui with a call of call_spel('callback function', 'spel method to call', 'first argument', 'second argument', ...)
op_add.spl just calls the spel_srvr method and forwards the given arguments to it. so you can use the same operation for all your needs. in call_spel.spl you can see how you can get the arguments and how deliver back some results to gui. if everthing works fine, the callback function will be called and you can get the results from spel in javascript (see call_spel_cb). once established, you can simply add new methods to call_spel.mod and call_spel.spl and call them from a htmpl form.

greetings,
pacy

Sep 05, 2019 12:38 PM

void z_dump_args(object zo_obj, ...)
{
////////////////////////////////////////////////////////////////////////
// Metodo: z_dump_args
// Autor: Daniel Becker Bighelini
// Criado em: 05/09/2019
// Modificado por: Daniel Becker Bighelini
// Modificado em: 05/09/2019
//
// Parametros de entrada:
// arg[0] : (object) zo_obj Objeto 'Connection_args'
// arg[1] : (int) zi_debug [OPCIONAL] Define o nivel de depuracao da funcao (0=Desligado, 1=Log resumido, 2=Log detalhado, 3=Super detalhado)
// arg[2] : (string) zs_method [OPCIONAL] Define o nome do metodo que esta chamando este metodo
//
// Resultado:
// Exibe em log todos os argumentos de conexao e seus rescpectivos valores.
// Esta funcao somente funciona corretamente no contexto de uma operacao WEBENGINE.
// Argumentos de conexao = ':INITIAL_LOAD:KEEP.POPUP_NAME:LREL_REMOVE_PERSIDS:OP:SID:ENV_DOCUMENT_ROOT:PERSID:KEEP.use_role:ENV_HTTP_REMOTE_USER:ENV_REMOTE_HOST:CALLBACK_FUNC:ENV_REMOTE_USER:FID:ENV_HTTP_USER_AGENT:POPUP_NAME:ENV_SCRIPT_NAME:ENV_REQUEST_METHOD:ENV_HTTP_COOKIE:LREL_FACTORY:FACTORY:'
///////////////////////////////////////////////////////////////////////////////////////
// Inicio do codigo
int zi_debug;
string zs_method;
zs_method = "z_dump_args";
if (argc>1) {zi_debug = (int) argv[1];}
if (argc>2) {zs_method = format("%s %s", (string) argv[2], zs_method);}
send_wait(0, zo_obj, "get_arg_list");
if (msg_error()) {
logf(ERROR, "%s ERRO ao retornar os argumentos de conexao: '%s'", zs_method, msg[0]);

} else {
string zs_connection_args;
zs_connection_args = msg[0];

string zs_arg, zs_value, zs_delim, zs_regex;
zs_delim = ':';
zs_regex = '[aA-zZ]+';

int zi_idx_start, zi_idx_end;
zi_idx_start = sindex(zs_connection_args, zs_regex, 0);
zi_idx_end = sindex(zs_connection_args, zs_delim, zi_idx_start+1);

do {
zs_arg = substr(zs_connection_args, zi_idx_start, zi_idx_end-zi_idx_start);

send_wait(0, zo_obj, "call_attr", zs_arg, "get_val");
if (msg_error()) {
logf(ERROR, "%s ERRO ao consultar o atributo '%s': '%s'", zs_method, zs_arg, msg[0]);

} else {
zs_value = msg[0];
logf(SIGNIFICANT, "%s %s = '%s'", zs_method, zs_arg, zs_value);
}

zi_idx_start = sindex(zs_connection_args, zs_regex, zi_idx_end+1);
zi_idx_end = sindex(zs_connection_args, zs_delim, zi_idx_start+1);
} while (zi_idx_start > -1);
}
// Fim do codigo
///////////////////////////////////
}

Mar 22, 2018 09:57 AM

Hi cdtj,

 

Thank you for the reply.  I am familiar with using Update with  popupActivityWithURL and popupWithURL which I am currently using the latter but I would really like to forgo the Update OP and use my own directly since I am updating multiple tickets at one time and adding required status transition log comment.

 

Kerry

Mar 20, 2018 03:51 PM

Hi,

there is no need to implement custom spel method, try to use regular OP=UPDATE, then specify decided form as HTMPL=cr_resolve_all_child.htmpl, so complete code could be:

function zResolveAllChild() {
     var query_str = w.cfgCgi + "?SID=" + w.cfgSID +
          "+FID=" + w.fid_generator() +
          "+FACTORY=" + factory +
          "+SET.id=" + w.argID +
          "+PERSID=" + w.argPersistentID +
          "+OP=UPDATE" +
          "+HTMPL=cr_resolve_all_child.htmpl" +
          "+RESOLVE_ALL_CHILD=1";
     popupActivityWithURL(query_str);
}
<PDM_OBJECT>
if ( typeof w.ahdframeset.ahdframe._dtl == "object" &&
w.ahdframeset.ahdframe._dtl.edit==false) {
     <PDM_MACRO name=menuItemLocal label="Resolve_All_Children..." function="zResolveAllChild();">
}
</PDM_OBJECT>

Regards,

cdtj

 

Mar 20, 2018 12:19 PM

Hi cdtj,

 

Thank you for the guidance.  All of your examples use the ahdframeset.workframe from a button.  I would like to use Action menu item and create a Resolve version of  'Close All Children'. using a new form 'cr_resolve_all_child.htmpl'.  I have not been able to get the form to appear in the popup window.  I am using the menubar_sd.htmpl code.  It does not recognize the form I pass when I try to add the HTMPL in query_str.  Could you please advise how I call the form in spl to be viewed.  Thanks.-

 

var query_str = w.cfgCgi + "?SID=" + w.cfgSID +
"+FID=" + w.fid_generator() +
"+FACTORY=" + factory +
"+SET.id=" + w.argID +
"+PERSID=" + w.argPersistentID;

 

<PDM_OBJECT>
if ( typeof w.ahdframeset.ahdframe._dtl == "object" &&
w.ahdframeset.ahdframe._dtl.edit==false) {
<PDM_MACRO name=menuItemLocal label="Resolve_All_Children..." function="JavaScript: invokeMenuItem('\" + query_str + \"+RESOLVE_ALL_CHILD=1\" + \"')">
}
</PDM_OBJECT>

Mar 12, 2018 11:58 AM

Thanks

I've done this way:

 

result = 'parent.ahdframe.alert("My alert message");\n';

send_frame_resp((long) some_id, (object) new_obj, (string) result);

Feb 14, 2018 12:17 PM

hi,

I recommend to call:

send_frame_resp((long)some_id, (object)new_obj, (string)new_js);

on each WebEngine OP, where new_js is any data that can be returned to callback function or manually handled by any other tool.

 

Here is an example from last script, I'm not good at js (and ajax, and jquery and other js based tools) but hope it will be helpful:

// JavaScript part
// here I do ajax query and receive JSON as string, if JSON object have errorMsg attr I show it
function zAjaxBackfill(group_id) {
     set_action_in_progress(ACTN_AUTOFILL);
     var url = cfgCgi +
          "?SID=" + cfgSID +
          "+FID=" + fid_generator() +
          "+OP=Z_GET_LAZY_TECH" +
          "+FACTORY=" + "$args.producer_id" +
          "+PERSID=" + "$args.persistent_id" +
          "+CALLBACK=null" +
          "+POPUP_NAME=" + '$args.KEEP.POPUP_NAME' +
          "+GROUP_ID=" + group_id +
          "";
     jq.ajax({
          type: 'GET',
          url: url,
          data: { get_param: 'value' },
          dataType: 'text',
          success: function (data) {
               var sindex = data.lastIndexOf("<json>");
               var eindex = data.indexOf("</json>", sindex);
               if (sindex != -1 && eindex != -1) {
                    var _script = data.substring(sindex + 6, eindex);
               }
              try {
                    zRetObj = JSON.parse(_script);
               } catch (e) {
                    alert("Ошибка:" + e);
               }
               if ((typeof(zRetObj.errorMsg) != "undefined") && (typeof(zRetObj) != null)) {
                    alert(zRetObj.errorMsg);
               } else {
                    // doSomething
               }
          }
     });
     set_action_in_progress(ACTN_COMPLETE);
}
// spl side
// here I do some work and string by string building my JSON
#define MSG_ERROR for(msg_i=0;msg_i<msg_length();msg_i++) { logf(ERROR, "msg[%d]: %s", msg_i, msg[msg_i]); }
#define MSG_SIGN for(msg_i=0;msg_i<msg_length();msg_i++) { logf(SIGNIFICANT, "msg[%d]: %s", msg_i, msg[msg_i]); }
#define MSG_BOTH if (msg_error()) { MSG_ERROR } else { MSG_SIGN }

z_get_lazy_tech(...) {
     long some_id;
     object new_obj;
     string result;

     some_id = argv[0];
     new_obj = argv[1];

     result = z_backfill_call(new_obj.FACTORY, (uuid)new_obj.GROUP_ID);
     send_frame_resp((long)some_id, (object)new_obj, (string)result);
}

string z_backfill_call(string factory, uuid group_id) {
     string result;
     result = "<json>{\n";
     
     result += format('"errorMsg", "this is error description"\n');

     result += "}\n</json>";
     logf(TRACE, result);
     return result;
}

Feb 14, 2018 12:02 PM

Hi,

 

Thanks for sharing this.

Just a question, how can we add a popup alert to the end-user if the operation has completed with success.

I added an if to check if the user belongs to the group of the ticket:

 

 logf(SIGNIFICANT, "%s > group", persid);
 send_wait(0, top_object(), "call_attr", "grpmem", "sync_fetch", "DYNAMIC", format("member = U'%s' AND group = U'%s'", user, cr_dob.group.id), -1, 0);
 MSG_BOTH
 has_grp= msg[1];
 logf(SIGNIFICANT, "%s > HAS group", has_grp);
 
  if (msg[1] > 0) {
  logf(SIGNIFICANT, "%s > [%s] is member of [%s]", persid, user, cr_dob.group.last_name);
     logf(SIGNIFICANT, "%s > set_val", persid);
     send_wait(0, cr_dob, "call_attr", "assignee", "set_val", user, "SURE_SET");
     MSG_BOTH
  

     logf(SIGNIFICANT, "%s > is_co [in-progress]", persid);
     send_wait(0, cr_dob, "is_co");
     MSG_BOTH

     logf(SIGNIFICANT, "%s > is_co", persid);
     send_wait(0, gl, "checkin");
     MSG_BOTH   
     } 
  else{
   //ALERT USER -> FAIL
  }

 

How can I achieve this?

 

Thank you in advance.

Jan 16, 2018 06:45 PM

Hi colleagues,

 

Need some help. I need spel which when triggered by button from the chg will complete the workflow task.

Can any one help?

Dec 16, 2017 04:59 AM

hi,

I have an issue with one of my scripts where method val_by_key fails due to user session expiration but it affects only cosmetic side so i didnt really tried to find a workaround.

As a workaround you can try to call your methods bypassing top_object(), for example using spell_object(): Execute SDM method using SPEL 

Regards,

cdtj

Dec 16, 2017 02:33 AM

cdtj do you have any experience how to impersonate user in webengine operation. We have made some dashboards for the users that works for licensed users, but does not work for unlicensed.

Jul 13, 2017 09:29 AM

hi,

could you check what happens with workframe (part of ahdframeset used to load the code) using browser source code?

Also you can add adittional logfs to check which method fails.

Regards,

cdtj

Jul 13, 2017 08:53 AM

Hi,

I applied this step.But After a while I get an "delayed response" error on the list screen.what is the reason of this?I dont see any other error in stdlog files.

Regards,Burcu

 

 

 

 

Jun 22, 2017 02:36 PM

Everything seems to be ok. Object being checked out correctly and checked in... Here is techdoc regarding to your error: What does the error "AHD04423: ERROR: Another user has changed the record since it was first displayed" imply? When this… 

 

Firstly I thound that error can be caused due to post actions like POST_CI, that are not finished when clicking the button.

Now I think that you need to use (if you still wan't to debud this method ) more personal method instead of simple "set_val".

It could be:

// FileName: Service Desk Manager\bopcfg\majic\cm.maj
logf(SIGNIFICANT, "%s > transfering", persid);
send_wait(0, cr_dob, "transfer",
     gl,                         // Group Leader
     user,                    // Who
     "Self-Transfer",     // Description
     user,                    // New Assignee
     cr_dob.group,          // New Group
     NULL);                    // New alg dob

Method requires checkin call.

 

Regards,

cdtj

Jun 22, 2017 09:04 AM

Thank you CDTJ!

 

The z_assign function seems to always be successful. send_frame_resp is intermittently failing.  Here's the log output from an unsuccessful run followed by a successful run. send_frame_resp fails if I go into edit, remove the assignee, save, then press Assign to Me.  

 

Unsuccessful

06/22 05:58:26.42 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 8 some_id: 33
06/22 05:58:26.43 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 11 Persid: cr:400352 Contact: 279C5B7B5C99F64C81FBD9EBDEC3536B
06/22 05:58:26.43 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 21 Assign to User started
06/22 05:58:26.43 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 25 cr:400352 > dob_by_persid
06/22 05:58:26.44 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 27 msg[0]:
06/22 05:58:26.44 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 27 msg[1]: 0
06/22 05:58:26.44 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 27 msg[2]: cr:400352
06/22 05:58:26.45 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 30 cr:400352 > is_co [initial]
06/22 05:58:26.45 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 32 msg[0]: NOT_CO
06/22 05:58:26.46 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 34 cr:400352 > get_co_group
06/22 05:58:26.46 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 36 msg[0]:
06/22 05:58:26.46 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 39 cr:400352 > checkout
06/22 05:58:26.47 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 41 msg[0]: OK
06/22 05:58:26.48 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 41 msg[1]:
06/22 05:58:26.48 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 43 cr:400352 > set_val
06/22 05:58:26.48 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[0]: OK
06/22 05:58:26.49 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[1]: 279C5B7B5C99F64C81FBD9EBDEC3536B
06/22 05:58:26.49 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[2]: WOCM
06/22 05:58:26.49 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[3]: SURE_SET
06/22 05:58:26.50 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 47 cr:400352 > is_co [in-progress]
06/22 05:58:26.50 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 49 msg[0]: IS_CO
06/22 05:58:26.50 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 51 cr:400352 > is_co
06/22 05:58:26.53 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 53 msg[0]: OK
06/22 05:58:26.53 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 55 Assign to User ended
06/22 05:58:26.67 chq-grantb-l8 web:local 11464 ERROR freeaccess.spl 24588 Record has changed beneath us. Can't do update: request # 63 object='cr' persid='cr:400352' common name='63' original timestamp=06/21/2017 16:22:26 record's timestamp=06/22/2017 05:58:26 user that got error='ServiceDesk'

 

Successful run

06/22 05:59:35.81 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 8 some_id: 57
06/22 05:59:35.82 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 11 Persid: cr:400352 Contact: 279C5B7B5C99F64C81FBD9EBDEC3536B
06/22 05:59:35.82 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 21 Assign to User started
06/22 05:59:35.82 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 25 cr:400352 > dob_by_persid
06/22 05:59:35.83 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 27 msg[0]:
06/22 05:59:35.83 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 27 msg[1]: 0
06/22 05:59:35.84 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 27 msg[2]: cr:400352
06/22 05:59:35.84 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 30 cr:400352 > is_co [initial]
06/22 05:59:35.84 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 32 msg[0]: NOT_CO
06/22 05:59:35.85 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 34 cr:400352 > get_co_group
06/22 05:59:35.85 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 36 msg[0]:
06/22 05:59:35.86 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 39 cr:400352 > checkout
06/22 05:59:35.87 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 41 msg[0]: OK
06/22 05:59:35.87 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 41 msg[1]:
06/22 05:59:35.88 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 43 cr:400352 > set_val
06/22 05:59:35.88 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[0]: OK
06/22 05:59:35.88 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[1]: 279C5B7B5C99F64C81FBD9EBDEC3536B
06/22 05:59:35.89 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[2]: WOCM
06/22 05:59:35.89 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 45 msg[3]: SURE_SET
06/22 05:59:35.89 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 47 cr:400352 > is_co [in-progress]
06/22 05:59:35.90 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 49 msg[0]: IS_CO
06/22 05:59:35.90 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 51 cr:400352 > is_co
06/22 05:59:35.92 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 53 msg[0]: OK
06/22 05:59:35.93 chq-grantb-l8 web:local 11464 SIGNIFICANT zAssign.spl 55 Assign to User ended

Jun 22, 2017 02:26 AM

Hi,

I have added log shootout after each send_wait. Hope this give more info.

#define MSG_ERROR for(msg_i=0;msg_i<msg_length();msg_i++) { logf(ERROR, "msg[%d]: %s", msg_i, msg[msg_i]); }
#define MSG_SIGN for(msg_i=0;msg_i<msg_length();msg_i++) { logf(SIGNIFICANT, "msg[%d]: %s", msg_i, msg[msg_i]); }
#define MSG_BOTH if (msg_error()) { MSG_ERROR } else { MSG_SIGN }

zAssign(...) {
     long some_id;
     some_id = argv[0];
     logf(SIGNIFICANT, "some_id: %s", some_id);
     object new_obj;
     new_obj = argv[1];
     logf(SIGNIFICANT, "Persid: %s Contact: %s", new_obj.PERSID, new_obj.CURRENT_USER);
     string callback;
     callback = "parent.ahdframe.location.reload();\n";
     z_assign((uuid) new_obj.CURRENT_USER, (string) new_obj.PERSID);
     send_frame_resp((long) some_id, (object) new_obj, (string) callback);
}

z_assign(uuid user, string persid) {
     sting method;
     method = "Assign to User";
     logf(SIGNIFICANT, "%s started", method);
     object cr_dob, gl;
     int msg_i;

     logf(SIGNIFICANT, "%s > dob_by_persid", persid);
     send_wait(0, top_object(), "call_attr", "cr", "dob_by_persid", 0, persid);
     MSG_BOTH
     cr_dob = msg[0];

     logf(SIGNIFICANT, "%s > is_co [initial]", persid);
     send_wait(0, cr_dob, "is_co");
     MSG_BOTH

     logf(SIGNIFICANT, "%s > get_co_group", persid);
     send_wait(0, top_object(), "get_co_group");
     MSG_BOTH
     gl = msg[0];

     logf(SIGNIFICANT, "%s > checkout", persid);
     send_wait(0, gl, "checkout", cr_dob);
     MSG_BOTH

     logf(SIGNIFICANT, "%s > set_val", persid);
     send_wait(0, cr_dob, "call_attr", "assignee", "set_val", user, "SURE_SET");
     MSG_BOTH

     logf(SIGNIFICANT, "%s > is_co [in-progress]", persid);
     send_wait(0, cr_dob, "is_co");
     MSG_BOTH

     logf(SIGNIFICANT, "%s > is_co", persid);
     send_wait(0, gl, "checkin");
     MSG_BOTH

     logf(SIGNIFICANT, "%s ended", method);
}

 

Regards,

cdtj

Jun 21, 2017 07:21 PM

Hi cdtj,

 

Thank you for sharing!  I'm working on my first custom webengine operation.  I've got this mostly working but found a bug that I'm not sure how to resolve.  This operation will set the assignee on a request/incident/problem to the logged in user (spl and js shared below).  If I open an incident which doesn't have an assignee and press Assign to Me, the spl is triggered and the form is refreshed with the assignee filled in.  However, if I open an incident that has an assignee in edit mode, remove the assignee, save, then press Assign to Me,  I receive an error message. The spl is run without error but the form is left in edit mode with an error.  Not sure what causes this.. Any help is appreciated.

Record has changed beneath us. Can't do update: request # 63 object='cr' persid='cr:400352' common name='63' original timestamp=06/21/2017 16:03:47 record's timestamp=06/21/2017 16:04:02 user that got error='ServiceDesk'

 

 

HTMPL

<PDM_IF "$prop.form_name_3" != "edit" && "$args.assignee" == "">
function zAssign() {
var url = cfgCgi +
"?SID=" + cfgSID +
"+FID=" + fid_generator() +
"+OP=Z_ASSIGN" +
"+FACTORY=cr" +
"+PERSID=" + argPersistentID +
"+POPUP_NAME=" + '$args.KEEP.POPUP_NAME' +
"+KEEP.POPUP_NAME=" + '$args.KEEP.POPUP_NAME' +
"+CURRENT_USER=" + "$cst.id" +
"+CALLBACK=null";
display_new_page(url, ahdframeset.workframe);
}
<PDM_MACRO name=button Caption="Assign to Me" Func="zAssign()" hotkey_name="ASSIGN[A]" ID=ASSIGN>
</PDM_IF>

 

SPL

zAssign(...)
{
     long some_id;
     some_id = argv[0];
      logf(SIGNIFICANT, "some_id: %s", some_id);
     object new_obj;
     new_obj = argv[1];
     logf(SIGNIFICANT, "Persid: %s Contact: %s", new_obj.PERSID, new_obj.CURRENT_USER);
      string callback;
      callback = "parent.ahdframe.location.reload();\n";
     z_assign((uuid)new_obj.CURRENT_USER, (string)new_obj.PERSID);
     send_frame_resp((long)some_id, (object)new_obj, (string)callback);
}

z_assign(uuid user, string persid)
{
     sting method;
     method = "Assign to User";
     logf(SIGNIFICANT, "%s started", method);
     //send_wait(0, top_object(), "call_attr", "api", "update_object_super", user, persid, 0, "assignee", user);
     //logf(SIGNIFICANT, "%s ended", method);
    object cr_dob, gl; 
    int msg_i; 
     
     // Get DOB 
     send_wait(0, top_object(), "call_attr", "cr", "dob_by_persid", 0, persid); 
     cr_dob = msg[0];

     send_wait(0, top_object(), "get_co_group"); 
     if (msg_error()) { 
          for (msg_i=0;msg_i<msg_length();msg_i++) { 
               logf(ERROR, "[%s] > msg[%d]: %s", persid, msg_i, msg[msg_i]); 
               return; 
          } 
     } 
     gl = msg[0]; 
          
     send_wait(0, gl, "checkout", cr_dob);
     if (msg_error())
     {
     logf(ERROR, "Error checking out dob, error: %s", msg[0]);
     return;
     }
     cr_dob.assignee=user;
     send_wait(0, gl, "checkin");
     logf(SIGNIFICANT, "%s ended", method);     
     
}

Jan 20, 2016 01:56 AM

hi,

 

here is a question how lookups (and dtlHiers) are work,

there is KEY.<attr> visible input with clickable header,

and hidden SET.<attr> with persid value.

 

When onchange event are trigger, you can get KEY.attr (onchange="console.log(this.value)"), and this could work if you're on unique name field, but how about contacts?

I would not dare to build query over combo_name to fetch contact, because how much John Smiths could be found in enveronment with 30k+ active users?

 

So we need to catch change of hidden input (SET.<attr>) to fetch persid but there is HTML engine limitation that gives no possibility to do that,

workaround solution is to switch all inputs with type=HIDDEN to type=TEXT with style display:none.

 

Other workaround that could work - after chaning KEY attr ootb function is filling SET attr and this switches form into action_in_progress state,

so you can trigger your onchange event with little delay (setTimeout) and check action_in_progress state before load your OP into background page.

Here is function sample to call detailSave when form is loaded:

function saveOnLoad() {

    function saveIfTrue() {

        if (currentAction == 0) {

            clearInterval(myInterval);

            detailSave();

        }

    }

    if (ahdframe.propFormName3 == 'edit') {

        var myInterval = setInterval(saveIfTrue, 100);

    } else {

        // console.log('$args.new_status == COMP | ' + '$prop.form_name_3 == edit')

    }

}

 

<BODY class="detailro" onUnload="unloadActions();" onload="loadActions();saveOnLoad();">

 

Third way is to get correct macro to submit form,

I have tried but with no success...

Jan 19, 2016 12:51 PM

Hi cdtj,

 

I'm doing some experiments with custom webengines, and I'm trying to call OPs from a lookup field event (onChange event), but without success. I tried to put it inside check_submit function too (which is executed when you hit Save button), no success too.

 

The fact is, you code works well in a button or frame that is already loaded in the page. But I can't put it to run/load after the loaded page.

 

This is what I wanted to achieve: when a user (analyst view) is opening a ticket (call_req), the window must warn when already exists an open ticket for the same customer, by raising a confirm() function in Javascript: "Hey! It already exists an open ticket to this customer! Do you really want to open another ticket to him?". So, I created an OP to verify if there is an open ticket for that customer, and I would call this OP either on onChange event in Customer field or when Save button is hit (that's why I wanted to modify the check_submit function), but I'm failing to call the OP.

 

Any idea?

Dec 01, 2015 11:50 PM

Hi Utkua,

nope, there is no documentation, this is just a method which I had luck to extract,

I'm using it in this way:

- add function to detail_*.htmpl form (example: function z_test(value) { console.log(value); })

- define callback to call that function: new_obj.CALLBACK="ahdframe.z_test(my_data);"

- define "my_data" as 3rd param to send_frame_resp;

 

More detailed example where return data defined as JS Object:

[SPL PART]

...

string result;

result = 'var return_data = {';

for (i=0;i<msg_length();i++) {

result += format("%s: '%s',\n", msg[i], z_get_attr_vals(zobj, msg[i]));

}

return result + "'': ''};";

...

new_obj.CALLBACK = "ahdframe.z_test(return_data);";

send_frame_resp((long)some_id, (object)new_obj, (string)result);

[HTML PART]

function z_test(value) {

console.log(value);

}

// id: "12345"

// ref_num: "23456"

// customer.combo_name: "ServiceDesk"

// etc

 

I'll publish full method soon, it will be universal backfill method.

Dec 01, 2015 02:30 PM

hi cdtj ,

 

is there any documentation about CALLBACK parameter? I checked wiki and docs but couldn't see anyting.

Is it possible to call more than 1 js functions / method with CALLBACK parameter?

For example I want to alert some message and also disable a button. Is it possible with CALLBACK?

Oct 19, 2015 04:01 PM

Our latest customization that uses custom webengine operation thanks cdtj  !!!

Sep 02, 2015 03:10 AM

my bad, I'll update doc.

Sep 02, 2015 02:53 AM

format_to_js, was the rootcaus ef my errors, i removed unescaping and now everything works fine

Sep 01, 2015 10:49 AM

sounds strange because this works fine in my env (12.7),

I have test server with 14.1 so I'll try this example there.

Sep 01, 2015 10:44 AM

I have executed it from detail_cr. I have copied everything from Your example.

Sep 01, 2015 10:39 AM

hm, what place you have used to load this url?

this code should be loaded in existing frame, I use ahdframe.workframe for this purposes.

Sep 01, 2015 10:30 AM

I have tried your example with the call back, bu I get this result:

<SCRIPT LANGUAGE='JavaScript' SRC='/CAisd/scripts/window_manager.js'></SCRIPT>

<SCRIPT>

var myMsg=\'Hello World!\';var ahdtop = get_ahdtop(true);

if ( typeof ahdtop == 'object' ) {

   var win = ahdtop.AHD_Windows['USD1441117300801'];

  if ( typeof win == 'object' )  win.alert(myMsg);

}

</SCRIPT>

 

Why "<html><head>" is missing?

 

Aug 07, 2015 08:00 AM

Have added usage example,

one more coming soon.

Jul 09, 2015 04:05 PM

Nice!!!!

Jun 30, 2015 09:06 AM

It works!!!

Jun 30, 2015 02:32 AM

z_call_autoassign.spl

z_call_assign(...)
{
     int some_id;
     some_id = argv[0];
     object new_obj;
     new_obj = argv[1];
     new_obj.CALLBACK="ahdframe.location.reload();";
     z_assign((string)new_obj.ISSCAT, (uuid)new_obj.REQ_ORG, (int)new_obj.ISS_ID);
     send_frame_resp((long)some_id, (object)new_obj, (string)"");
}

z_assign(string isscat, uuid req_org, int iss_id)
{
     string assign_wc;
     object assign_obj, assign_found;
     int assign_count, i;
     uuid new_assignee, new_group;
     
     if (is_null(req_org)) {
          assign_wc = format("org is NULL AND isscat='%s'", isscat);
     } else {
          assign_wc = format("(org=U'%s' OR org is NULL) AND isscat='%s'", req_org, isscat);
     }

     send_wait(0, top_object(), "call_attr", "z_assign", "sync_fetch", "STATIC", assign_wc, -1, 0);
     assign_count = msg[1];
     if (assign_count>0) {
          for (i=0;i<assign_count;i++) {
               assign_found = msg[0];
               send_wait(0, assign_found, "dob_by_index", "DEFAULT", i, i);
               assign_obj=msg[0];
               if (assign_obj.org == (uuid)req_org) {
                    new_assignee = assign_obj.assignee;
                    new_group = assign_obj.grp;
               } else if (is_null(assign_obj.org) && is_null(new_assignee)) {
                    new_assignee = assign_obj.assignee;
                    new_group = assign_obj.grp;
               }
          }
     }
     if (!is_null(new_assignee) && !is_null(new_group)) {
          upd_val("iss", format("id=%d", iss_id), 3, 3, "group", new_group, "assignee", new_assignee);
     } else {
          upd_val("iss", format("id=%d", iss_id), 3, 3, "group", (uuid)"D28C2A8220F545499FBCD8B9BD3C6D81", "assignee", (uuid)"DC90158A5E7B264D983E0DF1B0CA5A7C");
     }
}

 

I also used SPEL: Object update,

all of files should be located in site/mods/www,

upd_val function should be rename to upd_val (without macro::)

Jun 30, 2015 01:27 AM

hi,

sure!

 

Here is example of autoassignment function:

our customers asked to make an HR portal based on CA SDM and they are using Issue factory which haven't location based autoassignment,

so they asked to make new function which can assign tickets based on customers organization.

 

Document comments haven't attachment function, so here is raw code...

 

html code:

     var url = cfgCgi +
     "?SID=" + cfgSID +
     "+FID=" + fid_generator() +
     "+OP=Z_CALL_ASSIGN" +
     "+FACTORY=cr" +
     "+PERSID=" + argPersistentID +
     "+ISSCAT=" + "$args.category" +
     "+REQ_ORG=" + "$args.requestor.organization" +
     "+ISS_ID=" + "$args.id" +
     "+POPUP_NAME=" + '$args.KEEP.POPUP_NAME' +
     "+KEEP.POPUP_NAME=" + '$args.KEEP.POPUP_NAME' +
     "+CALLBACK=null";
     <PDM_MACRO name=button Caption="Автоназначение" Func="display_new_page(url, ahdframeset.workframe);" hotkey_name="TEST_OP[P]" ID=TEST_OP>

 

op_custom.cfg

     Z_CALL_ASSIGN z_call_assign MODIFY UPDATE

Jun 29, 2015 03:37 PM

cdtj i would like to get your opinion if it is possible to use this approach to define custom OP that will change some attribute value and reload current form?

Jun 24, 2015 08:31 AM

Outside temperature grows up to 40* celsus, so I haven't any reasons to leave office room.

This gave me possibility to find a way how to use callback function, now custom OP can modify entire page.

Jun 11, 2015 04:30 PM

Wow that is awesome! I will definitely play with this. I already can imagine what kind of customizations this will allow to perform.

Related Entries and Links

No Related Resource entered.