Advertisement
Guest User

Untitled

a guest
Apr 16th, 2014
248
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 162.62 KB | None | 0 0
  1. <sara xmlns="http://www.automic.com/sara/snsc">
  2.  
  3. <references>
  4.  
  5. <variable_types>
  6.  
  7. <var_type value="1">Yes / No</var_type>
  8.  
  9. <var_type value="2">Multi Line Text</var_type>
  10.  
  11. <var_type value="3">Multiple Choice</var_type>
  12.  
  13. <var_type value="4">Numeric Scale</var_type>
  14.  
  15. <var_type value="18">Lookup Select Box</var_type>
  16.  
  17. <var_type value="5">Select Box</var_type>
  18.  
  19. <var_type value="6">Single Line Text</var_type>
  20.  
  21. <var_type value="7">CheckBox</var_type>
  22.  
  23. <var_type value="8">Reference</var_type>
  24.  
  25. <var_type value="9">Date</var_type>
  26.  
  27. <var_type value="10">Date/Time</var_type>
  28.  
  29. <var_type value="11">Label</var_type>
  30.  
  31. <var_type value="12">Break</var_type>
  32.  
  33. <var_type value="14">Macro</var_type>
  34.  
  35. <var_type value="15">UI Page</var_type>
  36.  
  37. <var_type value="16">Wide Single Line Text</var_type>
  38.  
  39. <var_type value="17">Macro with Label</var_type>
  40.  
  41. <var_type value="21">List Collector</var_type>
  42.  
  43. <var_type value="22">Lookup Multiple Choice</var_type>
  44.  
  45. <var_type value="23">HTML</var_type>
  46.  
  47. </variable_types>
  48.  
  49. </references>
  50.  
  51. <package name="snsc" version="1.0-SNAPSHOT">
  52. <description/>
  53.  
  54. <system_properties>
  55. <category>
  56. <name>SARA</name>
  57. <title>This category consists of all SARA OAuth client settings</title>
  58. <properties>
  59. <property>
  60. <name>com.automic.sara.sapi_endpoint</name>
  61. <description/>
  62. <type>string</type>
  63. <value>http://vvnarasara01:8686/</value>
  64. <ignore_cache>false</ignore_cache>
  65. <private>false</private>
  66. </property>
  67. <property>
  68. <name>com.automic.sara.client_secret</name>
  69. <description/>
  70. <type>password2</type>
  71. <value>AES_12345</value>
  72. <ignore_cache>false</ignore_cache>
  73. <private>false</private>
  74. </property>
  75. <property>
  76. <name>com.automic.sara.client_id</name>
  77. <description/>
  78. <type>string</type>
  79. <value>SERVICE_NOW</value>
  80. <ignore_cache>false</ignore_cache>
  81. <private>false</private>
  82. </property>
  83. <property>
  84. <name>com.automic.sara.auth_endpoint</name>
  85. <description/>
  86. <type>string</type>
  87. <value>http://172.16.36.40:8080/oauth-server</value>
  88. <ignore_cache>false</ignore_cache>
  89. <private>false</private>
  90. </property>
  91. <property>
  92. <name>com.automic.sara.token_endpoint</name>
  93. <description/>
  94. <type>string</type>
  95. <value>http://vvnarasara01:8080/oauth-server/oauth2/token</value>
  96. <ignore_cache>false</ignore_cache>
  97. <private>false</private>
  98. </property>
  99. <property>
  100. <name>com.automic.sara.mid_server</name>
  101. <description/>
  102. <type>string</type>
  103. <value>VVNARAWIN01</value>
  104. <ignore_cache>false</ignore_cache>
  105. <private>false</private>
  106. </property>
  107. <property>
  108. <name>com.automic.sara.ref_scheme</name>
  109. <description/>
  110. <type>string</type>
  111. <value>relative</value>
  112. <ignore_cache>false</ignore_cache>
  113. <private>false</private>
  114. </property>
  115. </properties>
  116. </category>
  117. </system_properties>
  118.  
  119.  
  120. <tables>
  121. <table>
  122. <name>u_sara_oauth</name>
  123. <label>Sara OAuth</label>
  124. <extends_table/>
  125. <user_role>admin</user_role>
  126. <is_extendable>false</is_extendable>
  127. <dictionary_entries>
  128. <column>
  129. <name>u_access_token</name>
  130. <type>string</type>
  131. <label>Access Token</label>
  132. <max_length>1000</max_length>
  133. </column>
  134. <column>
  135. <name>u_refresh_token</name>
  136. <type>string</type>
  137. <label>Refresh Token</label>
  138. <max_length>1000</max_length>
  139. </column>
  140. <column>
  141. <name>u_token_expiry</name>
  142. <type>string</type>
  143. <label>Access Token</label>
  144. <max_length>40</max_length>
  145. </column>
  146. <column>
  147. <name>u_token_type</name>
  148. <type>string</type>
  149. <label>Token Type</label>
  150. <max_length>40</max_length>
  151. </column>
  152. <column>
  153. <name>u_user</name>
  154. <type>reference</type>
  155. <label>User</label>
  156. <max_length>32</max_length>
  157. <reference>sys_user</reference>
  158. </column>
  159. </dictionary_entries>
  160. </table>
  161. <table>
  162. <name>u_sara_variable</name>
  163. <label>Sara Variable</label>
  164. <extends_table>item_option_new</extends_table>
  165. <user_role>admin</user_role>
  166. <is_extendable>false</is_extendable>
  167. <dictionary_entries>
  168. <column>
  169. <name>u_name</name>
  170. <type>string</type>
  171. <label>Actual Name</label>
  172. <max_length>1000</max_length>
  173. </column>
  174. <column>
  175. <name>u_url</name>
  176. <type>string</type>
  177. <label>URL</label>
  178. <max_length>1000</max_length>
  179. </column>
  180. <column>
  181. <name>u_tooltip</name>
  182. <type>string</type>
  183. <label>Tooltip</label>
  184. <max_length>1000</max_length>
  185. </column>
  186. <column>
  187. <name>u_readonly</name>
  188. <type>boolean</type>
  189. <label>Read only</label>
  190. <max_length>40</max_length>
  191. </column>
  192. <column>
  193. <name>u_is_password</name>
  194. <type>boolean</type>
  195. <label>Is password</label>
  196. <max_length>40</max_length>
  197. </column>
  198. </dictionary_entries>
  199. </table>
  200. <table>
  201. <name>u_sara_definition</name>
  202. <label>Sara Definition</label>
  203. <extends_table/>
  204. <user_role>admin</user_role>
  205. <is_extendable>false</is_extendable>
  206. <dictionary_entries>
  207. <column>
  208. <name>u_name</name>
  209. <type>string</type>
  210. <label>Name</label>
  211. <max_length>1000</max_length>
  212. </column>
  213. <column>
  214. <name>u_links</name>
  215. <type>string</type>
  216. <label>Links</label>
  217. <max_length>2000</max_length>
  218. </column>
  219. <column>
  220. <name>u_consume_url</name>
  221. <type>string</type>
  222. <label>Consume URL</label>
  223. <max_length>1000</max_length>
  224. </column>
  225. <column>
  226. <name>u_sc_item</name>
  227. <type>reference</type>
  228. <label>Service Catalog Item</label>
  229. <max_length>32</max_length>
  230. <reference>sc_cat_item</reference>
  231. </column>
  232. <column>
  233. <name>u_sara_var_set</name>
  234. <type>reference</type>
  235. <label>Sara Variable Set</label>
  236. <max_length>32</max_length>
  237. <reference>item_option_new_set</reference>
  238. </column>
  239. <column>
  240. <name>u_parent</name>
  241. <type>reference</type>
  242. <label>Parent Category</label>
  243. <max_length>32</max_length>
  244. <reference>sc_category</reference>
  245. </column>
  246. <column>
  247. <name>u_type</name>
  248. <type>string</type>
  249. <label>Sara Type</label>
  250. <max_length>40</max_length>
  251. </column>
  252. </dictionary_entries>
  253. </table>
  254. </tables>
  255.  
  256.  
  257. <script_includes>
  258. <script_include>
  259. <name>SaraRestClient</name>
  260. <content><![CDATA[gs.include('SaraCommon');
  261. gs.include('SaraOAuthClient');
  262.  
  263. var SaraRestClient = Class.create();
  264. SaraRestClient.prototype = {
  265. initialize: function() {
  266. this.basePath = SaraCommon.getBasePath();
  267. if (!this.basePath) { gs.log('No SAPI Endpoint was defined, the REST client may not act correctly!', 'SARA') }
  268. },
  269.  
  270. newServiceRequest: function(name, method, endpoint) {
  271. // construct new service definition request
  272. var restMsg = new SaraRESTMessage(name, method);
  273. if (endpoint) restMsg.setRestEndPoint(endpoint);
  274. if (this.midServer) {
  275. restMsg.setMIDServer(this.midServer);
  276. }
  277.  
  278. return restMsg;
  279. },
  280.  
  281. newServiceRequest: function(name, method, endpoint, basicAuthCreds, headers) {
  282. // construct new service definition request
  283. var restMsg = new SaraRESTMessage(name, method);
  284. if (endpoint) restMsg.setRestEndPoint(endpoint);
  285. if (this.midServer) {
  286. restMsg.setMIDServer(this.midServer);
  287. }
  288.  
  289. if (basicAuthCreds && basicAuthCreds.length > 1) {
  290. restMsg.setBasicAuth(basicAuthCreds[0], basicAuthCreds[1]);
  291. }
  292.  
  293. if (headers) {
  294. for (index in headers) {
  295. var header = headers[index];
  296. if (header['name']) restMsg.addHeader(header['name'], header['value']);
  297. }
  298. }
  299.  
  300. return restMsg;
  301. },
  302.  
  303. newEntryRequest: function() {
  304. return this.newServiceRequest('Entry', 'get', this.basePath);
  305. },
  306.  
  307. newSapiGetRequest: function(relPath) {
  308. return this.newServiceRequest('Sapi', 'get', this.basePath + relPath);
  309. },
  310.  
  311. execute: function(saraRest) {
  312. if (this.midServer) return saraRest.executeViaMidServer();
  313. else return saraRest.execute();
  314. },
  315.  
  316. executeRequest: function(saraRest){
  317. // var request = saraRest;
  318. // firstime request.
  319. var response = this.execute(saraRest);
  320.  
  321. if (!response || response.getStatusCode() < 200 || response.getStatusCode() > 299) {
  322. // handle ERROR
  323. if (response && response.getStatusCode() == 401) {
  324. var resObj = new JSONParser().parse(response.getBody());
  325. var error = resObj['error'];
  326. var oauthClient = new SaraOAuthClient(this);
  327. if (error == 'expired_token') {
  328. // issue a refresh request
  329. var username = gs.getUserName();
  330. var tokens = oauthClient.refreshToken(username);
  331. if (tokens && !tokens.error) {
  332. oauthClient.storeUserTokens(username, tokens['access_token'], tokens['refresh_token'], tokens['expires_in'], tokens['token_type']);
  333. gs.log('Refresh token finished successfully.','SARA');
  334.  
  335. // request again with new token
  336. // var request = this.newServiceRequest('SAPI','get',saraRest.getEndpoint());
  337. saraRest.addHeader('Authorization', 'Bearer '+tokens['access_token']);
  338. gs.log('Request = '+'','SARA');
  339. response = null;
  340. response = this.execute(saraRest);
  341.  
  342. } else {
  343. gs.log('Refresh token finished unsuccessfully.');
  344. if (tokens != null) {
  345. gs.log('Error: ' + tokens.error);
  346. oauthClient.removeUserToken(username);
  347. }
  348. response = null;
  349. }
  350. } else {
  351. gs.log('You\'ve got invalid tokens, please try again or contact administrator to solve this problem');
  352. // invalidate token
  353. oauthClient.removeUserToken(username);
  354. response = null;
  355. }
  356. }
  357. }
  358. return response;
  359. },
  360.  
  361. executeWithRefreshToken: function(saraRest){
  362. //var request = saraRest;
  363. // firstime request.
  364. var response = this.execute(saraRest);
  365.  
  366. if (!response || response.getStatusCode() < 200 || response.getStatusCode() > 299) {
  367. // handle ERROR
  368. if (response && response.getStatusCode() == 401) {
  369. var resObj = new JSONParser().parse(response.getBody());
  370. var error = resObj['error'];
  371. var oauthClient = new SaraOAuthClient(this);
  372. var username = gs.getUserName();
  373. if (error == 'expired_token') {
  374. // issue a refresh request
  375. var tokens = oauthClient.refreshToken(username);
  376. if (tokens && !tokens.error) {
  377. oauthClient.storeUserTokens(username, tokens['access_token'], tokens['refresh_token'], tokens['expires_in'], tokens['token_type']);
  378. gs.log('Refresh token finished successfully.','SARA');
  379.  
  380. // request again with new token
  381. var request = this.newServiceRequest('SAPI','get',saraRest.getEndpoint());
  382. request .addHeader('Authorization', 'bearer '+tokens['access_token']);
  383. response = this.execute(request );
  384.  
  385. } else {
  386. gs.log('Refresh token finished unsuccessfully.');
  387. if (tokens != null) {
  388. gs.log('Error: ' + tokens.error);
  389. oauthClient.removeUserToken(username);
  390. }
  391.  
  392. }
  393. } else {
  394. gs.log('You\'ve got invalid tokens, please try again or contact administrator to solve this problem');
  395. // invalidate token
  396. oauthClient.removeUserToken(username);
  397.  
  398. }
  399. }
  400. }
  401.  
  402. return response;
  403. },
  404.  
  405. setBasePath: function(basePath) {
  406. this.basePath = basePath;
  407. },
  408.  
  409. getBasePath: function() {
  410. return this.basePath;
  411. },
  412.  
  413. setMIDServer: function(midServer) {
  414. this.midServer = midServer;
  415. },
  416. midServer: '',
  417. basePath: '',
  418. type: 'SaraRestClient'
  419. }
  420.  
  421. var SaraRESTMessage = Class.create();
  422.  
  423. SaraRESTMessage.prototype = Object.extendsObject(RESTMessage, {
  424.  
  425. setContent: function(content) {
  426. this.content = content;
  427. },
  428.  
  429. encodeUrl: function(value) {
  430. if (value) return Packages.java.net.URLEncoder.encode(value, 'utf-8');
  431. else return '';
  432. },
  433.  
  434. setFormContent: function(entity) {
  435. if (!entity) return;
  436. var content = '';
  437. for (prop in entity) {
  438. var value = entity[prop];
  439. if (value instanceof Array) {
  440. for (idx in value) content += (this.encodeUrl(prop) + '=' + this.encodeUrl(value[idx]) + '&');
  441. } else {
  442. content += (this.encodeUrl(prop) + '=' + this.encodeUrl(value) + '&');
  443. }
  444. }
  445. if (content.length > 0) {
  446. content = content.substring(0, content.length - 1);
  447. this.setContent(content);
  448. }
  449. },
  450.  
  451. addHeader: function(key, value) {
  452. if (!this.headers) this.headers = {};
  453. this.headers[key] = value;
  454. },
  455.  
  456. setContentType: function(type) {
  457. this.addHeader('content-type', type);
  458. },
  459.  
  460. setStringParameterWithoutEscaping: function (name, value) {
  461. this.props.put(name, value);
  462. },
  463.  
  464. executeViaMidServer: function(midServer) {
  465.  
  466. gs.log("Making " + this.funcName.toUpperCase() + " request to " + this.getEndpoint() + " ...", 'SARA');
  467.  
  468. if (midServer) this.setMIDServer(midServer)
  469.  
  470. if(!this.midServer) {
  471. gs.log('No MID Server defined for the request, abort now.', 'SARA');
  472. return null;
  473. }
  474.  
  475. this.execute();
  476. var k = 1;
  477. var response = this.getResponse();
  478. gs.log('Waiting for result from ' + this.getEndpoint(), 'SARA');
  479. while (response == null) {
  480. //gs.log("Waiting ... " + k + " seconds", 'SARA');
  481. response = this.getResponse(1000);
  482. k++;
  483.  
  484. if (k > 30) break;
  485. }
  486.  
  487. if (response != null) {
  488. if (response.getStatusCode()) gs.log('Status code: ' + response.getStatusCode(), 'SARA');
  489. else {
  490. gs.log('No returned code from server, error message: ' + response.errorMessage, 'SARA');
  491. }
  492. } else gs.log('No response from server', 'SARA');
  493.  
  494. return response;
  495. },
  496.  
  497. _handleContent: function() {
  498. var content;
  499. if (this.content) content = this.content;
  500. else content = '' + this.functionGr.content;
  501. if (this._shouldSubstitute(content))
  502. content = this._substituteVariable(content, this.props);
  503. return content;
  504. },
  505.  
  506. _handleHeaders: function() {
  507. var headers = {};
  508. var headersToReturn = [];
  509.  
  510. // Grab all the headers from the main message body first, then the function and replace any duplicates with the one
  511. // from the function because it takes precedent by being more specific
  512. var hgr = new GlideRecord('sys_rest_message_headers');
  513. hgr.addQuery('rest_message', this.restMessageGR.sys_id);
  514. hgr.query();
  515.  
  516. while (hgr.next())
  517. headers['' + hgr.name] = '' + hgr.value;
  518.  
  519. hgr = new GlideRecord('sys_rest_message_fn_headers');
  520. hgr.addQuery('rest_message_function', '' + this.functionGr.sys_id);
  521. hgr.query();
  522.  
  523. while (hgr.next())
  524. headers['' + hgr.name] = '' + hgr.value;
  525.  
  526. if (this.headers) {
  527. for (var key in this.headers) {
  528. headers['' + key] = this.headers[key];
  529. }
  530. }
  531.  
  532. for (var key in headers) {
  533. var value = headers[key];
  534. if (this._shouldSubstitute(value))
  535. value = this._substituteVariable(value, this.props);
  536. var header = {};
  537. header.name = key;
  538. header.value = value;
  539. headersToReturn.push(header);
  540. }
  541.  
  542. //gs.log("HEADERS:");
  543. //for (index in headersToReturn) {
  544. // gs.log(headersToReturn[index].name + " = " + headersToReturn[index].value);
  545. // }
  546.  
  547. return headersToReturn;
  548. },
  549.  
  550. _getEccResponse: function() {
  551. var eccResponse = null;
  552. var ieccgr = new GlideRecord('ecc_queue');
  553. ieccgr.addQuery('response_to', this.outputq);
  554. ieccgr.query();
  555. if (ieccgr.next()) {
  556. eccResponse = new SaraRESTECCResponse(ieccgr);
  557. eccResponse.setEndpoint(this.endPoint);
  558. }
  559.  
  560. return eccResponse;
  561. },
  562.  
  563. execute : function () {
  564. var httpResponse = null;
  565. var response = 'error';
  566. this.httpStatus = null;
  567.  
  568. if (this.restMessageGR.sys_id) {
  569. this.functionGr = new GlideRecord('sys_rest_message_fn');
  570. this.functionGr.addQuery('rest_message', this.restMessageGR.sys_id);
  571. this.functionGr.addQuery('function_name', this.funcName);
  572. this.functionGr.query();
  573. } else this.functionGr = {};
  574. if ((this.restMessageGR.sys_id && this.functionGr.next()) || !this.restMessageGR.sys_id) {
  575. this._handleEndpoint();
  576. var headers = this._handleHeaders();
  577. var params = this._handleParameters();
  578.  
  579. if (this.functionGr.use_mid_server && !this.functionGr.use_mid_server.nil()) {
  580. this.use_ecc = true;
  581. if (!this.midServer)
  582. this.midServer = this.functionGr.use_mid_server.name;
  583. }
  584.  
  585. var creds = this._handleBasicAuth();
  586.  
  587. if (this.use_ecc) {
  588. // Build ECC queue payload
  589. var payload = new GlideXMLDocument('parameters');
  590. this._addParameterToPayload(payload, 'message_headers', this._getMessageFields(headers));
  591. this._addParameterToPayload(payload, 'message_parameters', this._getMessageFields(params));
  592.  
  593. for (var name in this.eccParameters)
  594. this._addParameterToPayload(payload, name, this.eccParameters[name]);
  595.  
  596. if (this.useBasicAuth) {
  597. if (creds) {
  598. var encrypter = new GlideEncrypter();
  599. this._addParameterToPayload(payload, 'rest_user', creds.user);
  600. this._addParameterToPayload(payload, 'rest_password', 'enc:' + encrypter.reencryptForAutomation(creds.password));
  601. }
  602. }
  603. // if the function takes content
  604. if (this.funcName == 'post' || this.funcName == 'put')
  605. this._addParameterToPayload(payload, 'content', this._handleContent());
  606. this._createECCQueueEntry(payload.toString());
  607. } else {
  608. var httpRequest = new GlideHTTPRequest(this.endPoint);
  609.  
  610. if (this.useBasicAuth) {
  611. if (creds) {
  612. var Encrypter = new GlideEncrypter();
  613. var userpassword = Encrypter.decrypt(creds.password);
  614. httpRequest.setBasicAuth(creds.user, userpassword);
  615. }
  616. }
  617.  
  618. // Pass the headers through
  619. for (var h = 0; h < headers.length; h++)
  620. httpRequest.addHeader(headers[h].name, headers[h].value);
  621.  
  622. // Pass the parameters through
  623. for (var i = 0; i < params.length; i++)
  624. httpRequest.addParameter(params[i].name, params[i].value);
  625.  
  626. if (this.funcName == 'get')
  627. httpResponse = this._handleGetRequest(httpRequest);
  628. else if (this.funcName == 'post')
  629. httpResponse = this._handlePostRequest(httpRequest, this._handleContent());
  630. else if (this.funcName == 'put')
  631. httpResponse = this._handlePutRequest(httpRequest, this._handleContent());
  632. else if (this.funcName == 'delete')
  633. httpResponse = this._handleDeleteRequest(httpRequest);
  634. }
  635. }
  636.  
  637. return httpResponse;
  638. },
  639.  
  640. type: 'SaraRESTMessage'
  641. });
  642.  
  643. var SaraRESTECCResponse = Class.create();
  644.  
  645. SaraRESTECCResponse.prototype = Object.extendsObject(RESTECCResponse, {
  646.  
  647. _processEccQueueInput: function(input) {
  648. var xml = new XMLHelper();
  649. var record = xml.toObject('' + input.payload);
  650. this.params = record.parameters.parameter;
  651. this.statusCode = this._getParameter('http_status_code');
  652. this.content = this._getParameter('content');
  653. /*
  654. this.hasError = false;
  655. this.errorCode = 0;
  656. this.errorMessage = '';
  657. */
  658. var error = record['@error'];
  659.  
  660. if (error) {
  661. this.hasError = true;
  662. this.errorMessage = error;
  663. }
  664. if (record.result) {
  665. var error = record.result['@error'];
  666. if (error) {
  667. this.hasError = true;
  668. this.errorMessage = error;
  669. }
  670.  
  671. error = record.result.error;
  672. if (error){
  673. this.hasError = true;
  674. this.errorMessage = error;
  675. }
  676.  
  677. this.body = record.result.output;
  678. }
  679.  
  680. },
  681.  
  682. type: 'SaraRESTECCResponse'
  683. });
  684.  
  685. SaraRESTECCResponse.prototype.toString = Object.prototype.toString;
  686. ]]></content>
  687. <description>Wraps and extends Service Now RESTMessage in a class to provide convenient methods interacting with SARA REST</description>
  688. <active>true</active>
  689. <client_callable>false</client_callable>
  690. </script_include>
  691. <script_include>
  692. <name>SaraOAuthClient</name>
  693. <content><![CDATA[gs.include('SaraCommon');
  694. var SaraOAuthClient = Class.create();
  695. SaraOAuthClient.prototype = {
  696. initialize: function(restClient) {
  697. if (!restClient) {
  698. this.restClient = new SaraRestClient();
  699. this.restClient.setMIDServer(SaraCommon.getMidServer());
  700. } else this.restClient = restClient;
  701. },
  702.  
  703. /**
  704. * Store SARA OAuth client settings to System Properties
  705. */
  706. storeClientSettings: function(clientId, clientSecret, sapiEndpoint, tokenEndpoint, authEndpoint) {
  707. // check for SARA category
  708. var catId = null;
  709. var catRec = new GlideRecord('sys_properties_category');
  710. catRec.addQuery('name', 'SARA');
  711. catRec.query();
  712. if (catRec.next()) catId = catRec.sys_id;
  713. else {
  714. catRec = new GlideRecord('sys_properties_category');
  715. catRec.initialize();
  716. catRec.name = 'SARA';
  717. catRec.title = 'This category consists of all SARA OAuth client settings';
  718. catId = catRec.insert();
  719. }
  720.  
  721. // save properties to 'sys_properties' table
  722. var props = ['client_id', 'client_secret', 'sapi_endpoint', 'token_endpoint', 'auth_endpoint'];
  723. var values = [clientId, clientSecret, sapiEndpoint, tokenEndpoint, authEndpoint];
  724. var propRec = null;
  725. var order = 50;
  726. for (index = 0; index < props.length; index++) {
  727. var propId = null;
  728. propRec = new GlideRecord('sys_properties');
  729. var propName = SaraCommon.PROP_PREFIX + '.' + props[index];
  730. propRec.addQuery('name', propName);
  731. propRec.query();
  732. if (propRec.next()) {
  733. propRec.value = values[index];
  734. propId = propRec.update();
  735. } else {
  736. propRec = new GlideRecord('sys_properties');
  737. propRec.initialize();
  738. propRec.name = propName;
  739. propRec.value = values[index];
  740. propId = propRec.insert();
  741. }
  742.  
  743. // ensure relationship between properties and 'SARA' category
  744. var catPropRec = new GlideRecord('sys_properties_category_m2m');
  745. catPropRec.addQuery('property', propId);
  746. catPropRec.addQuery('category', catId);
  747. catPropRec.query();
  748. if (!catPropRec.next()) {
  749. catPropRec = new GlideRecord('sys_properties_category_m2m');
  750. catPropRec.initialize();
  751. catPropRec.property = propId;
  752. catPropRec.category = catId;
  753. catPropRec.order = order;
  754. catPropRec.insert();
  755. order += 10;
  756. }
  757. }
  758.  
  759.  
  760. },
  761.  
  762. /**
  763. * Gets the array of SARA client settings in following order: Client Id, Client Secret, SAPI Endpoint,
  764. * Token Endpoint, Authorization Endpoint
  765. */
  766. getClientSettings: function() {
  767. return [SaraCommon.getSaraProperty('client_id'), SaraCommon.getSaraProperty('client_secret'), SaraCommon.getSaraProperty('sapi_endpoint'),
  768. SaraCommon.getSaraProperty('token_endpoint'), SaraCommon.getSaraProperty('auth_endpoint')]
  769.  
  770. },
  771.  
  772. getSaraProperty: function(name) {
  773. return gs.getProperty(SaraCommon.PROP_PREFIX + '.' + name);
  774. },
  775.  
  776. type: 'SaraOAuthClient'
  777. }
  778.  
  779. SaraOAuthClient.prototype.storeUserTokens = function(username, accessToken, refreshToken, tokenExpiry, tokenType) {
  780. if (!username) {
  781. gs.log('Store user token: Username must not be empty', 'SARA');
  782. return;
  783. }
  784. if (!accessToken) {
  785. gs.log('Error: Access token must be non-empty', 'SARA');
  786. return;
  787. }
  788. var rec = new GlideRecord('u_sara_oauth');
  789. rec.addQuery('u_user.user_name', username);
  790. rec.query();
  791. if (rec.next()) {
  792. rec.u_access_token = accessToken;
  793. rec.u_refresh_token = refreshToken;
  794. rec.u_token_expiry = tokenExpiry;
  795. rec.u_token_type = tokenType;
  796. return rec.update();
  797. } else {
  798. rec = new GlideRecord('u_sara_oauth');
  799. rec.initialize();
  800. var uRec = new GlideRecord('sys_user');
  801. uRec.addQuery('user_name', username);
  802. uRec.query();
  803. if (uRec.next()) rec.u_user = uRec.sys_id;
  804. else {
  805. gs.log('User \'' + username + '\' does not exist, skipped.', 'SARA');
  806. return;
  807. }
  808. rec.u_access_token = accessToken;
  809. rec.u_refresh_token = refreshToken;
  810. rec.u_token_expiry = tokenExpiry;
  811. rec.u_token_type = tokenType;
  812. return rec.insert();
  813. }
  814. }
  815.  
  816. SaraOAuthClient.prototype.getUserTokens = function(username) {
  817. if (!username) return null;
  818. var rec = new GlideRecord('u_sara_oauth');
  819. rec.addQuery('u_user.user_name', username);
  820. rec.query();
  821. if (rec.next()) {
  822. return [rec.u_access_token, rec.u_refresh_token, rec.u_token_expiry, rec.u_token_type,
  823. { 'createdAt': rec.sys_created_on, 'createdBy': rec.sys_created_by, 'updatedAt': rec.sys_updated_on, 'updatedBy': rec.sys_updated_by }];
  824. } else return null;
  825. }
  826.  
  827. SaraOAuthClient.prototype.removeUserToken = function(username) {
  828. if (!username) return null;
  829. var rec = new GlideRecord('u_sara_oauth');
  830. rec.addQuery('u_user.user_name', username);
  831. rec.query();
  832. if (rec.next()) {
  833. return rec.deleteRecord();
  834. } else {
  835. gs.log('No token found for user \'' + username, 'SARA');
  836. return null;
  837. }
  838. }
  839.  
  840. /**
  841. * Requests user tokens, the request must use client_id/client_secret to authenticate (BASICally) against SARA server,
  842. * LDAP username/password and grant_type will be embedded in request's body
  843. * @return array of access token, refresh token and token expiry
  844. */
  845. SaraOAuthClient.prototype.requestToken = function(username, password) {
  846.  
  847. var clientInfo = this.getClientSettings();
  848. var tokenEndpoint = clientInfo[3];
  849.  
  850. var request = this.restClient.newServiceRequest('Request Token', 'post', tokenEndpoint);
  851. request.setBasicAuth(clientInfo[0], clientInfo[1]);
  852.  
  853. // add POST body, set Content-Type header
  854. request.setContentType('application/x-www-form-urlencoded');
  855. var formEntity = {'grant_type' : 'password', 'username' : username, 'password': password};
  856.  
  857. //request.setContent('grant_type=password&username=' + username + '&password=' + password);
  858. request.setFormContent(formEntity);
  859.  
  860. gs.log('Requesting tokens for SARA user ' + username + ' ...', 'SARA');
  861.  
  862. var response = this.restClient.execute(request);
  863.  
  864. if (!response || response.getStatusCode() < 200 || response.getStatusCode() > 299) {
  865. gs.log('Failed to request token for user ' + username, 'SARA');
  866. if (response && response.getStatusCode()) {
  867. gs.log("Status code: " + response.getStatusCode() + ", error message: " + response.getBody(), 'SARA');
  868. var resObj = new JSONParser().parse(response.getBody());
  869. if (!resObj.error) resObj = {'error': 'unknown_error', 'error_description': 'Status code: ' + response.getStatusCode()};
  870. return {'error': resObj.error, 'error_description': resObj.error_description};
  871. } else {
  872. gs.log('No response from server', 'SARA');
  873. if (response && response.errorMessage) return {'error': 'no_response', 'error_description': response.errorMessage};
  874. else return null;
  875. }
  876. }
  877.  
  878. gs.log('Successfully obtain token for user ' + username, 'SARA');
  879.  
  880. return (new JSONParser()).parse(response.getBody());
  881. }
  882.  
  883. SaraOAuthClient.prototype.refreshToken = function(username) {
  884. // get refresh token for this user from 'sara_oauth' table
  885. var tokens = this.getUserTokens(username);
  886. if (!tokens || tokens.length < 1) {
  887. gs.log('Tokens not found for user ' + username, 'SARA');
  888. return null;
  889. }
  890.  
  891. // use that refesh token to retrieve new tokens
  892. var clientInfo = this.getClientSettings();
  893. var tokenEndpoint = clientInfo[3];
  894. var request = this.restClient.newServiceRequest('Refresh Token', 'post', tokenEndpoint);
  895. request.setBasicAuth(clientInfo[0], clientInfo[1]);
  896.  
  897. // add POST body, set Content-Type header
  898. //request.addHeader('content-type', 'application/x-www-form-urlencoded');
  899. request.setContentType('application/x-www-form-urlencoded');
  900. //request.setContent('grant_type=refresh_token&refresh_token=' + tokens[1]);
  901. request.setFormContent({'grant_type': 'refresh_token', 'refresh_token': tokens[1]});
  902.  
  903. gs.log('Refreshing tokens for user ' + username + ' ...', 'SARA');
  904.  
  905. var response = this.restClient.execute(request);
  906.  
  907. if (!response || response.getStatusCode() < 200 || response.getStatusCode() > 299) {
  908. gs.log('Failed to refresh token for user ' + username, 'SARA');
  909. if (response && response.getStatusCode()) {
  910. gs.log("Status code: " + response.getStatusCode() + ", error message: " + response.getBody(), 'SARA');
  911. var resObj = new JSONParser().parse(response.getBody());
  912. if (!resObj.error) resObj = {'error': 'unknown_error', 'error_description': 'Status code: ' + response.getStatusCode()};
  913. return {'error': resObj.error, 'error_description': resObj.error_description};
  914. } else {
  915. gs.log('No response from server', 'SARA');
  916. if (response && response.errorMessage) return {'error': 'no_response', 'error_description': response.errorMessage};
  917. else return null;
  918. }
  919.  
  920. }
  921.  
  922. gs.log('Successfully refresh token for user ' + username, 'SARA');
  923.  
  924. return (new JSONParser()).parse(response.getBody());
  925. }
  926. ]]></content>
  927. <description>This class contains all OAuth-relaled functions</description>
  928. <active>true</active>
  929. <client_callable>false</client_callable>
  930. </script_include>
  931. <script_include>
  932. <name>SaraCommon</name>
  933. <content><![CDATA[var SaraCommon = Class.create();
  934. SaraCommon.prototype = {
  935. initialize: function() {
  936.  
  937. },
  938.  
  939. type: 'SaraCommon'
  940. }
  941.  
  942. SaraCommon.PROP_PREFIX = 'com.automic.sara';
  943. SaraCommon.OAUTH_IMPORT_SUCCESS = 0;
  944. SaraCommon.OAUTH_AUTHENTICATION_FAILED = 1;
  945. SaraCommon.OAUTH_INVALID_TOKEN = 2;
  946. SaraCommon.OAUTH_TOKEN_EXPIRED = 3;
  947. SaraCommon.VAR_TYPE = {};
  948. SaraCommon.VAR_TYPE.YES_NO = 1;
  949. SaraCommon.VAR_TYPE.MULTI_LINE_TEXT = 2;
  950. SaraCommon.VAR_TYPE.MULTI_CHOICE = 3;
  951. SaraCommon.VAR_TYPE.NUM_SCALE = 4;
  952. SaraCommon.VAR_TYPE.LOOKUP_SELECT_BOX = 18;
  953. SaraCommon.VAR_TYPE.SINGLE_LINE_TEXT = 6;
  954. SaraCommon.VAR_TYPE.CHECK_BOX = 7;
  955. SaraCommon.VAR_TYPE.REFERENCE = 8;
  956. SaraCommon.VAR_TYPE.DATE = 9;
  957. SaraCommon.VAR_TYPE.DATE_TIME = 10;
  958. SaraCommon.VAR_TYPE.LABEL = 11;
  959. SaraCommon.VAR_TYPE.BREAK = 12;
  960. SaraCommon.VAR_TYPE.MACRO = 14;
  961. SaraCommon.VAR_TYPE.UI_PAGE = 15;
  962. SaraCommon.VAR_TYPE.WIDE_SINGLE_LINE_TEXT = 16;
  963. SaraCommon.VAR_TYPE.MACRO_WITH_LABEL = 17;
  964. SaraCommon.VAR_TYPE.CONTAINER_START = 19;
  965. SaraCommon.VAR_TYPE.CONTAINER_END = 20;
  966. SaraCommon.VAR_TYPE.LIST_COLLECTOR = 21;
  967. SaraCommon.VAR_TYPE.LOOKUP_MULTI_CHOICE = 22;
  968. SaraCommon.VAR_TYPE.HTML = 23;
  969.  
  970. SaraCommon.getSaraProperty = function(name) {
  971. return gs.getProperty(SaraCommon.PROP_PREFIX + '.' + name);
  972. };
  973.  
  974. SaraCommon.getBasePath = function() {
  975. return SaraCommon.getSaraProperty('sapi_endpoint');
  976. };
  977.  
  978. SaraCommon.getMidServer = function() {
  979. return SaraCommon.getSaraProperty('mid_server');
  980. };
  981.  
  982. SaraCommon.getRefScheme = function() {
  983. var scheme = SaraCommon.getSaraProperty('ref_scheme');
  984. return scheme?scheme:'relative';
  985. }
  986.  
  987.  
  988.  
  989. ]]></content>
  990. <description>This class is comprised of global parameters in SARA connector. It shouldn't be instantiated and fields will be accessed in static-like fashion (think Java), eg.: SaraCommon.username</description>
  991. <active>true</active>
  992. <client_callable>false</client_callable>
  993. </script_include>
  994. <script_include>
  995. <name>SaraCommonAjax</name>
  996. <content><![CDATA[var SaraCommonAjax = Class.create();
  997. SaraCommonAjax.prototype = Object.extendsObject(AbstractAjaxProcessor,{
  998. getSysProperty: function() {
  999. var propName = this.getParameter('sysparm_property_name');
  1000. return gs.getProperty(propName);
  1001. },
  1002. type : "SaraCommonAjax"
  1003. });
  1004. ]]></content>
  1005. <description>Get Sara common property on server side. This will be called from client side by using GlideAjax</description>
  1006. <active>true</active>
  1007. <client_callable>true</client_callable>
  1008. </script_include>
  1009. <script_include>
  1010. <name>SaraModel</name>
  1011. <content><![CDATA[var SaraModel = Class.create();
  1012. SaraModel.prototype = {
  1013. initialize: function() {
  1014. },
  1015.  
  1016. type: 'SaraModel'
  1017. }
  1018.  
  1019.  
  1020. /**
  1021. * CatalogItem object represents a SN service catalog item
  1022. * @constructor
  1023. */
  1024. function CatalogItem(name, category, active, workflow, shortDesc, description, variables, additionalFields) {
  1025. this.name = name;
  1026. this.category = category;
  1027. this.active = active;
  1028. this.workflow = workflow;
  1029. this.shortDescription = shortDesc;
  1030. this.description = description;
  1031. this.variables = variables;
  1032. this.additionalFields = additionalFields;
  1033. }
  1034.  
  1035. CatalogItem.prototype.setVariables = function(variables) {
  1036. this.variables = variables;
  1037. }
  1038.  
  1039. CatalogItem.prototype.getCategory = function() {
  1040. return this.category;
  1041. }
  1042.  
  1043. CatalogItem.prototype.getName = function() {
  1044. return this.name;
  1045. }
  1046.  
  1047. CatalogItem.prototype.setConsumeUrl = function(consumeUrl) {
  1048. this.consumeUrl = consumeUrl;
  1049. }
  1050.  
  1051. CatalogItem.prototype.getConsumeUrl = function() {
  1052. return this.consumeUrl;
  1053. }
  1054.  
  1055. CatalogItem.prototype.setLinks = function(links) {
  1056. this.links = links;
  1057. }
  1058.  
  1059. CatalogItem.prototype.getLinks = function() {
  1060. return this.links;
  1061. }
  1062.  
  1063. CatalogItem.prototype.setServiceName = function(serviceName) {
  1064. this.serviceName = serviceName;
  1065. }
  1066.  
  1067. CatalogItem.prototype.getServiceName = function() {
  1068. return this.serviceName;
  1069. }
  1070.  
  1071. CatalogItem.prototype.setCustomCart = function(customCart) {
  1072. this.customCart = customCart;
  1073. }
  1074.  
  1075. /**
  1076. * Checks if a given link name contained in catalog item definition
  1077. * @return true if catalog item contains given link name
  1078. */
  1079. CatalogItem.prototype.containLink = function(link) {
  1080. return this.links[link]?true:false;
  1081. }
  1082.  
  1083. /**
  1084. * Gets link object for given name
  1085. */
  1086. CatalogItem.prototype.getLink = function(link) {
  1087. return this.links[link];
  1088. }
  1089.  
  1090. function CatalogVariable(name, type, label, defaultValue, description, range) {
  1091. this.name = name;
  1092. this.type = type;
  1093. this.label = label;
  1094. this.defaultValue = defaultValue;
  1095. this.description = description;
  1096. this.range = range;
  1097. }
  1098.  
  1099. CatalogVariable.prototype.setRange = function(range) {
  1100. this.range = range;
  1101. }
  1102.  
  1103. CatalogVariable.prototype.setActualName = function(actualName) {
  1104. this.actualName = actualName;
  1105. }
  1106.  
  1107. CatalogVariable.prototype.setUrl = function(url) {
  1108. this.url = url;
  1109. }
  1110.  
  1111. CatalogVariable.prototype.isPassword = function(isPassword) {
  1112. this.isPassword = isPassword;
  1113. }
  1114.  
  1115. CatalogVariable.prototype.isRequired = function(required) {
  1116. this.required = required;
  1117. }
  1118. /**
  1119. * CatalogCategory object represents a SN service catalog category
  1120. * @constructor
  1121. */
  1122. function CatalogCategory(title, name, parent,active, description){
  1123. this.title = title;
  1124. this.name = name;
  1125. this.parent = parent;
  1126. this.active = active;
  1127. this.description = description;
  1128. }
  1129.  
  1130. CatalogCategory.prototype.setName = function(name){
  1131. this.name = name;
  1132. }
  1133.  
  1134. CatalogCategory.prototype.getName = function() {
  1135. return this.name;
  1136. }
  1137.  
  1138. CatalogCategory.prototype.setConsumeUrl = function(consumeUrl) {
  1139. this.consumeUrl = consumeUrl;
  1140. }
  1141.  
  1142. CatalogCategory.prototype.getConsumeUrl = function() {
  1143. return this.consumeUrl;
  1144. }
  1145. ]]></content>
  1146. <description>SARA concepts modelling in Service Now</description>
  1147. <active>true</active>
  1148. <client_callable>false</client_callable>
  1149. </script_include>
  1150. <script_include>
  1151. <name>SaraCatalogGenerator</name>
  1152. <content><![CDATA[gs.include('SaraUtils');
  1153. var SaraCatalogGenerator = Class.create();
  1154. SaraCatalogGenerator.prototype = {
  1155. initialize: function() {
  1156. this.glideHelper = new SaraGlideHelper();
  1157. },
  1158. save: function(itemObj) {
  1159. // creates new service catalog item
  1160. var rec = new GlideRecord('sc_cat_item');
  1161. rec.initialize();
  1162. rec.name = itemObj.name;
  1163. if (itemObj.category) {
  1164. rec.category = itemObj.category;
  1165. }
  1166.  
  1167. rec.active = itemObj.active;
  1168. if (itemObj.workflow) {
  1169. var workflowRec = new GlideRecord('wf_workflow');
  1170. workflowRec.addQuery('name', itemObj.workflow);
  1171. workflowRec.query();
  1172. if (workflowRec.next()) rec.workflow = workflowRec.sys_id;
  1173. }
  1174.  
  1175. if (itemObj.customCart) {
  1176. var cartRec = new GlideRecord('sys_ui_macro');
  1177. cartRec.addQuery('name', itemObj.customCart);
  1178. cartRec.query();
  1179. if (cartRec.next()) rec.custom_cart = cartRec.sys_id;
  1180. }
  1181.  
  1182. rec.short_description = itemObj.shortDescription;
  1183. rec.description = itemObj.description;
  1184.  
  1185. // TODO: process additional fields
  1186. var catId = rec.insertWithReferences();
  1187.  
  1188. var varSetId = null;
  1189. // process catalog variables
  1190. if (itemObj.variables.length > 0) {
  1191. // create a variable set containing all variables
  1192. var varSetRec = new GlideRecord('item_option_new_set');
  1193. varSetRec.initialize();
  1194. varSetRec.name = itemObj.name + '_VarSet';
  1195. varSetRec.description = 'Variable Set containing all variables belong to \'' + itemObj.name + '\' catalog item';
  1196. varSetRec.title = itemObj.name + ' Variable Set';
  1197. varSetRec.display_title = true;
  1198. // save variable set to 'item_option_new_set' table
  1199. var varSetId = varSetRec.insert();
  1200.  
  1201. // create relation between variable set and catalog item by adding a record to 'io_set_item' table
  1202. var varSetItemRec = new GlideRecord('io_set_item');
  1203. varSetItemRec.initialize();
  1204. varSetItemRec.sc_cat_item = catId;
  1205. varSetItemRec.variable_set = varSetId;
  1206. varSetItemRec.insertWithReferences();
  1207.  
  1208. // process each variable in the set
  1209. var order = 50;
  1210. for (i in itemObj.variables) {
  1211. this.saveVariable(itemObj.variables[i], null, varSetId, { 'order': order });
  1212. order += 10;
  1213. }
  1214.  
  1215. }
  1216.  
  1217. // add new row to 'u_sara_definition' table, holding additional info. about this services like consume URL, links
  1218. // and reference to the variable set that contains Sara fields
  1219. rec = new GlideRecord('u_sara_definition');
  1220. rec.initialize();
  1221. rec.u_sc_item = catId;
  1222. gs.log('SAVING SERVICE \'' + itemObj.getServiceName() + '\' ...', 'SARA');
  1223. rec.u_name = itemObj.getServiceName();
  1224. if (itemObj.consumeUrl) rec.u_consume_url = itemObj.consumeUrl;
  1225. if (itemObj.links) rec.u_links = itemObj.links;
  1226. if (varSetId) rec.u_sara_var_set = varSetId;
  1227. rec.u_parent = itemObj.category;
  1228. rec.u_type = 'service';
  1229. rec.insert();
  1230. },
  1231.  
  1232. /**
  1233. *
  1234. */
  1235. saveVariable: function(variableObj, catalogItemId, variableSetId, optionals) {
  1236. var varRec = new GlideRecord('u_sara_variable');
  1237. varRec.initialize();
  1238. varRec.name = variableObj.name;
  1239. varRec.type = variableObj.type;
  1240. if (catalogItemId) varRec.cat_item = catalogItemId;
  1241. else if (variableSetId) varRec.variable_set = variableSetId;
  1242.  
  1243. if (variableObj.description){
  1244. varRec.help_text = variableObj.description;
  1245. varRec.show_help = true;
  1246. }
  1247.  
  1248. varRec.default_value = variableObj.defaultValue;
  1249. varRec.question_text = variableObj.label;
  1250. varRec.mandatory = variableObj.required;
  1251.  
  1252. if (optionals) {
  1253. for (prop in optionals) varRec[prop] = optionals[prop];
  1254. }
  1255.  
  1256. // additional fields for Sara
  1257. varRec.u_name = variableObj.actualName;
  1258. varRec.u_url = variableObj.url;
  1259. varRec.u_is_password = variableObj.isPassword;
  1260.  
  1261. if (variableObj.range) {
  1262. // process variable range
  1263. // create a new table contain variable range
  1264. var tblName = 'u_sara_' + SaraUtils.randomNum() + '_' + variableObj.name.toLowerCase();
  1265. if (tblName.length > 30) tblName = tblName.substring(0, 30);
  1266. var lookupTableRec = new GlideRecord('sys_db_object');
  1267. if (!lookupTableRec.get('name', tblName)) {
  1268. lookupTableRec.initialize();
  1269.  
  1270. lookupTableRec.name = tblName;
  1271. var tblId = lookupTableRec.insert();
  1272.  
  1273. gs.print('Create table name \'' + tblName + '\' finished successfully.');
  1274. gs.print('Creating schema for table \'' + tblName + '\'..');
  1275.  
  1276. // create schema for table by inserting to table 'sys_dictionary'
  1277. var columns = ['u_key', 'u_display_name', 'u_description', 'u_icon'];
  1278. var columnLabels = ['Key', 'Display Name', 'Description', 'Icon'];
  1279. for (index in columns) {
  1280. var col = columns[index];
  1281. var dictRec = new GlideRecord('sys_dictionary');
  1282. dictRec.initialize();
  1283. dictRec.element = col;
  1284. dictRec.type = 'string';
  1285. dictRec.name = tblName;
  1286. dictRec.column_label = columnLabels[index];
  1287. dictRec.insert();
  1288. }
  1289.  
  1290. gs.print('Create schema for table \'' + tblName + '\' finished successfully.','SARA');
  1291. gs.print('Inserting values to table \'' + tblName + '\'..','SARA');
  1292.  
  1293. // insert values to lookup table
  1294. for (index in variableObj.range) {
  1295. var varValue = variableObj.range[index];
  1296. var lookupRowRec = new GlideRecord(tblName);
  1297. lookupRowRec.initialize();
  1298. ////lookupRowRec.u_key = varValue.key;
  1299. gs.print('Insert key: ' + varValue.Key ,'SARA');
  1300. lookupRowRec.u_key = varValue.Value;
  1301. lookupRowRec.u_display_name = varValue.Key;
  1302. //lookupRowRec.u_display_name = varValue.displayName;
  1303. //lookupRowRec.u_description = varValue.description;
  1304. //lookupRowRec.u_icon = varValue.icon;
  1305. lookupRowRec.insert();
  1306. }
  1307.  
  1308. gs.print('Insert value to table \'' + tblName + '\' finished successfully.');
  1309.  
  1310. varRec.lookup_table = tblName;
  1311. varRec.lookup_value = 'u_display_name';
  1312.  
  1313. }
  1314. gs.print('Updating variable..','SARA');
  1315.  
  1316. // update variable
  1317. ////varRec = new GlideRecord('u_sara_variable');
  1318. // varRec.addQuery('sys_id', varId);
  1319. // varRec.query();
  1320. //while(varRec.next()) {
  1321. // varRec.type = 18; // Lookup Select Box
  1322. // varRec.lookup_table = tblName;
  1323. // varRec.lookup_value = 'u_display_name';
  1324. // varRec.update();
  1325. // gs.print('Done!');
  1326. // }
  1327. }
  1328. // save variable to 'u_sara_variable' table
  1329. varRec.insertWithReferences();
  1330. },
  1331. /**
  1332. * Create catalog category
  1333. * @return Category ID from tbale sc_category
  1334. */
  1335. saveCategory: function(categoryEntity) {
  1336. var categoryRec = new GlideRecord('sc_category');
  1337. categoryRec.initialize();
  1338. categoryRec.title = categoryEntity.title;
  1339. if (categoryEntity.parent){
  1340. // Parent is reference type, need to query sys_id from the given parent_name
  1341. var parentRec = new GlideRecord('sc_category');
  1342. parentRec.addQuery('title',categoryEntity.parent);
  1343. parentRec.query();
  1344. if (parentRec.next()) categoryRec.parent = parentRec.sys_id;
  1345. }else{
  1346. // TODO: default root category is : 'Automic Integration Demo'
  1347. // if root category is not provided, then use the default.
  1348. var parentRec = new GlideRecord('sc_category');
  1349. var targetCategoryId;
  1350. parentRec.addQuery('title','Automic Integration Demo');
  1351. parentRec.query();
  1352. if (parentRec.next()) targetCategoryId = parentRec.sys_id;
  1353.  
  1354. if (typeof wizard != 'undefined' && wizard && wizard.sara_target_category){
  1355. gs.log("Importing SAPI catalogs into category "+wizard.sara_target_category+" ...");
  1356. targetCategoryId = wizard.sara_target_category;
  1357. }
  1358. categoryRec.parent = targetCategoryId;
  1359. }
  1360. // Assign default homepage renderer
  1361. var rendererRec = new GlideRecord('sc_homepage_renderer');
  1362. rendererRec.addQuery('Name', 'Default');
  1363. if (rendererRec.next()) categoryRec.homepage_renderer = rendererRec.sys_id;
  1364.  
  1365. // Active this category
  1366. categoryRec.active = categoryEntity.active;
  1367.  
  1368. // Add description
  1369. categoryRec.description = categoryEntity.description;
  1370.  
  1371. var catId = categoryRec.insertWithReferences();
  1372. gs.log("Created Catalog category "+categoryRec.title+" with sys_id = "+catId);
  1373.  
  1374.  
  1375. // add new row to 'u_sara_definition' table, holding additional info. about this category
  1376. var rec = new GlideRecord('u_sara_definition');
  1377. rec.initialize();
  1378. rec.u_service_catalog_category = catId;
  1379. rec.u_name = categoryEntity.name;
  1380. if (categoryRec.getConsumeUrl()) rec.u_consume_url = categoryRec.getConsumeUrl();
  1381. rec.u_parent = categoryRec.parent;
  1382. rec.u_type = 'catalog';
  1383. rec.insert();
  1384.  
  1385. return catId;
  1386. },
  1387.  
  1388. /**
  1389. *
  1390. */
  1391. updateItem: function(item) {
  1392. if (!item.sysId) return null;
  1393. gs.log('Updating item , name = ' + item.name + ', short description = ' + item.shortDescription, 'SARA');
  1394. // update item info
  1395. this.glideHelper.updateData('sc_cat_item', {'sys_id': item.sysId}, {
  1396. 'name': item.name,
  1397. 'short_description': item.shortDescription,
  1398. 'description': item.description,
  1399. });
  1400.  
  1401. // update corresponding record in 'u_sara_definition' table
  1402. var saraDef = this.glideHelper.getData('u_sara_definition', {'u_sc_item': item.sysId}, ['sys_id', 'u_sara_var_set']);
  1403. if (saraDef && saraDef.length > 0) {
  1404. saraDef = saraDef[0];
  1405. var varSetId = saraDef['u_sara_var_set'];
  1406. // process catalog variables
  1407. if (item.variables.length > 0) {
  1408. // delete all existing variables in current set
  1409. this.glideHelper.deleteData('u_sara_variable', {'variable_set': varSetId}, true);
  1410.  
  1411. // process new variables in the set
  1412. var order = 50;
  1413. for (i in item.variables) {
  1414. this.saveVariable(item.variables[i], null, varSetId, {
  1415. 'order' : order
  1416. });
  1417. order += 10;
  1418. }
  1419. }
  1420.  
  1421. this.glideHelper.updateData('u_sara_definition', {'u_sc_item': saraDef['sys_id']}, {'u_consume_url': item.getConsumeUrl(), 'u_sara_var_set': varSetId});
  1422.  
  1423. }
  1424.  
  1425. // update item variables/variable set
  1426. // delete old variable set and all related variableSetId
  1427.  
  1428.  
  1429. // create new variables, variable set and wire with item
  1430.  
  1431.  
  1432. },
  1433.  
  1434. /**
  1435. *
  1436. */
  1437. updateCategory: function(category) {
  1438. if (!category.sysId) return null;
  1439. gs.log('Updating category, title = ' + category.title + ', description = ' + category.description, 'SARA');
  1440. return this.glideHelper.updateData('sc_category', {'sys_id': category.sysId}, {
  1441. 'title': category.title,
  1442. 'description': category.description
  1443. });
  1444. },
  1445.  
  1446. type: 'SaraCatalogGenerator'
  1447. };
  1448. ]]></content>
  1449. <description>Service Catalog Generator to import service definition to Service Catalog</description>
  1450. <active>true</active>
  1451. <client_callable>false</client_callable>
  1452. </script_include>
  1453. <script_include>
  1454. <name>SaraOAuthAjax</name>
  1455. <content><![CDATA[var SaraOAuthAjax = Class.create();
  1456. SaraOAuthAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
  1457.  
  1458. requestToken: function() {
  1459. var username = this.getParameter('sysparm_username');
  1460. var password = this.getParameter('sysparm_password');
  1461.  
  1462. var oauthClient = new SaraOAuthClient();
  1463. var tokens = oauthClient.requestToken(username, password);
  1464.  
  1465. if (tokens && !tokens.error && tokens['access_token']) {
  1466. oauthClient.storeUserTokens(gs.getUserName(), tokens['access_token'], tokens['refresh_token'], tokens['expires_in'], tokens['token_type']);
  1467. return 'ok';
  1468. } else return tokens.error ? tokens.error : (!tokens['access_token']?'Empty result':'');
  1469. },
  1470.  
  1471. helloWorld: function() {
  1472. gs.sleep(5000);
  1473. return "Hello " + this.getParameter('sysparm_username') + "!";
  1474. },
  1475. });
  1476. ]]></content>
  1477. <description>AJAX facade for SaraOAuthClient</description>
  1478. <active>true</active>
  1479. <client_callable>true</client_callable>
  1480. </script_include>
  1481. <script_include>
  1482. <name>SaraUtils</name>
  1483. <content><![CDATA[gs.include('SaraModel');
  1484. gs.include('SaraCommon');
  1485.  
  1486. var SaraUtils = Class.create();
  1487. SaraUtils.prototype = {
  1488. initialize: function() {
  1489.      }, 
  1490.  
  1491. type: 'SaraUtils'
  1492. }
  1493.  
  1494. SaraUtils.randomNum = function() {
  1495. return new Date().valueOf();
  1496. }
  1497.  
  1498. SaraUtils.cleanup = function() {
  1499. // get list of catalog item from 'u_sara_definiton' table
  1500. var rec = new GlideRecord('u_sara_definition');
  1501. rec.query();
  1502. var catItemIds = [];
  1503. var varSetIds = [];
  1504. var catCateIds = [];
  1505. while (rec.next()) {
  1506. if (rec.u_sc_item) catItemIds.push(rec.u_sc_item.sys_id);
  1507. if (rec.u_sara_var_set) varSetIds.push(rec.u_sara_var_set.sys_id);
  1508. if (rec.u_service_catalog_category) catCateIds.push(rec.u_service_catalog_category.sys_id);
  1509. }
  1510.  
  1511. var index;
  1512.  
  1513. // remove all variables belong to above variable sets
  1514. for (index = 0; index < varSetIds.length; index++) {
  1515. //
  1516. gs.log('Removing variables from variable set ' + varSetIds[index] + ' ...','SARA');
  1517. rec = new GlideRecord('item_option_new');
  1518. rec.addQuery('variable_set', varSetIds[index]);
  1519. rec.deleteMultiple();
  1520. }
  1521.  
  1522. // if a variable has lookup table, remove the table (skip now)
  1523.  
  1524. // remove all variable sets
  1525. if (varSetIds.length > 0) {
  1526. gs.log('Removing ' + varSetIds.length + ' variable sets ...','SARA');
  1527. for (index = 0; index < varSetIds.length; index++) {
  1528. rec = new GlideRecord('item_option_new_set');
  1529. rec.addQuery('sys_id', varSetIds[index]);
  1530. rec.query();
  1531. if (rec.next()) rec.deleteRecord();
  1532. }
  1533. rec.deleteMultiple();
  1534. }
  1535.  
  1536. // cleanup service catalog items
  1537. if (catItemIds.length > 0) {
  1538. gs.log('Removing ' + catItemIds.length + ' catalog items ...','SARA');
  1539. for (index = 0; index < catItemIds.length; index++) {
  1540. rec = new GlideRecord('sc_cat_item');
  1541. rec.addQuery('sys_id', catItemIds[index]);
  1542. rec.query();
  1543. if (rec.next()) rec.deleteRecord();
  1544. }
  1545. }
  1546.  
  1547. // cleanup catalog categories
  1548. if (catCateIds.length > 0) {
  1549. gs.log('Removing ' + catCateIds.length + ' catalog categories ...','SARA');
  1550. for (index = 0; index < catCateIds.length; index++) {
  1551. rec = new GlideRecord('sc_category');
  1552. rec.addQuery('sys_id', catCateIds[index]);
  1553. rec.query();
  1554. if (rec.next()) rec.deleteRecord();
  1555. }
  1556. }
  1557.  
  1558. gs.log('Cleaning up u_sara_definition table ...','SARA');
  1559. rec = new GlideRecord('u_sara_definition');
  1560. rec.deleteMultiple();
  1561.  
  1562. gs.log('Cleaning up u_sara_variable table ...','SARA');
  1563. rec = new GlideRecord('u_sara_variable');
  1564. rec.deleteMultiple();
  1565. }
  1566.  
  1567. SaraUtils.dropSaraRangeTables = function() {
  1568. // query all table entries from 'sys_db_object' that begins with 'u_sara_'
  1569. var rec = new GlideRecord('sys_db_object');
  1570. rec.addQuery('name', 'STARTSWITH', 'u_sara_');
  1571. rec.addQuery('name', 'NOT IN', 'u_sara_variable,u_sara_oauth,u_sara_definition');
  1572. rec.query();
  1573. var count = 1;
  1574. var tables = [];
  1575. while (rec.next()) {
  1576. var index;
  1577.  
  1578. // for each table, retrieve all related entries from 'sys_dictionary' and 'sys_documentation' to delete
  1579.  
  1580. var sys_id = rec.sys_id;
  1581. var tblName = rec.name;
  1582.  
  1583. gs.log(count++ + ': table = ' + tblName)
  1584. // query 'sys_dictionary'
  1585. var rec1 = new GlideRecord('sys_dictionary');
  1586. rec1.addQuery('name', tblName);
  1587. rec1.query();
  1588. //rec.deleteMultiple();
  1589. //rec.query();
  1590.  
  1591. while (rec1.next()) {
  1592. gs.log(' - ' + rec1.column_label + ' ' + rec1.element);
  1593. rec1.deleteRecord();
  1594. }
  1595.  
  1596. var rec2 = new GlideRecord('sys_documentation');
  1597. rec2.addQuery('name', tblName);
  1598. rec2.query();
  1599. //rec.deleteMultiple();
  1600.  
  1601. while(rec2.next()) {
  1602. gs.log(' + ' + rec2.element);
  1603. rec2.deleteRecord();
  1604. }
  1605.  
  1606. rec.deleteRecord();
  1607. }
  1608. }
  1609.  
  1610. SaraUtils.createCatalogVariable = function(str) {
  1611. var field =str;
  1612. var variable;
  1613. var defaultValue = '';
  1614. if ( field.detault.length > 0 ) {
  1615. defaultValue = field.detault;
  1616. }
  1617.  
  1618. switch (field.type) {
  1619. case 'Label':
  1620. gs.log("Create Label Field: "+this.legitVarName(field.name),'SARA');
  1621. var varType = SaraCommon.VAR_TYPE.LABEL;
  1622. break;
  1623. case 'Number':
  1624. gs.log("Create Number Field: "+this.legitVarName(field.name),'SARA');
  1625. // SN only have NUM_SCALE variable type but not suitable, just use string
  1626. var varType = SaraCommon.VAR_TYPE.SINGLE_LINE_TEXT;
  1627. break;
  1628. case 'String':
  1629. gs.log("Create String Field: "+this.legitVarName(field.name),'SARA');
  1630. var varType = SaraCommon.VAR_TYPE.SINGLE_LINE_TEXT;
  1631. break;
  1632. case 'List':
  1633. gs.log("Create List Field: "+this.legitVarName(field.name),'SARA');
  1634. var varType = SaraCommon.VAR_TYPE.LOOKUP_SELECT_BOX;
  1635. break;
  1636. case 'Time':
  1637. gs.log("Create Time Field: "+this.legitVarName(field.name),'SARA');
  1638. var varType = SaraCommon.VAR_TYPE.SINGLE_LINE_TEXT;
  1639. break;
  1640. case 'Date':
  1641. gs.log("Create Date Field: "+this.legitVarName(field.name),'SARA');
  1642. var varType = SaraCommon.VAR_TYPE.DATE;
  1643. break;
  1644. case 'Timestamp':
  1645. gs.log("Create Timestamp Field: "+this.legitVarName(field.name),'SARA');
  1646. var varType = SaraCommon.VAR_TYPE.DATE_TIME;
  1647. break;
  1648. default:
  1649. break;
  1650. }
  1651. variable = new CatalogVariable(this.legitVarName(field.name), varType, field.caption, defaultValue, field.description, []);
  1652. if (field.maskAsPassword) variable.isPassword(field.maskAsPassword);
  1653. if (field.range && field.range.values) {
  1654. if (!(field.range.values instanceof Array)) field.range.values = [field.range.values];
  1655. variable.setRange(field.range.values);
  1656. }
  1657. if (field.name) variable.setActualName(field.name);
  1658. if (field.required) variable.isRequired(field.required);
  1659. return variable;
  1660. }
  1661. /**
  1662. * Contructs a CatalogItem object from its definition
  1663. * @return CatalogItem object
  1664. */
  1665. SaraUtils.fromDef = function(defStr) {
  1666. // TODO: parses the definition using JSONParser
  1667. var parser = new JSONParser();
  1668. var catObj = parser.parse(defStr);
  1669.  
  1670. gs.log("TQT: Form length "+ catObj.forms.length );
  1671. var i;
  1672. for( i=0; i < catObj.forms.length;i++ ) {
  1673. gs.log("TQT: Form length"+ i );
  1674. }
  1675. var obj = catObj.forms[0];
  1676. for( i=0; i < obj.fields.length;i++ ) {
  1677. gs.log("TQT: Form length "+ obj.fields[i].type );
  1678.  
  1679. }
  1680. //if (!catObj['shortDescription']) catObj['shortDescription'] = 'This is auto-generated shortDescription for service ' + catObj['name'];
  1681. var catItem = new CatalogItem(catObj['name'], 'SARA Integration Demo', true, 'Sara Service', catObj['shortDescription'], catObj['shortDescription'], [], '');
  1682.  
  1683. //catItem.setLinks(catObj['_links']);
  1684. catItem.setServiceName(catObj['name']);
  1685.  
  1686. // set SARA item to use 'sara_cart' cart
  1687. catItem.setCustomCart('sara_cart');
  1688. gs.log("TQT:"+ catObj.forms.length );
  1689.  
  1690. var variables = [];
  1691. var field = catObj.forms[0].fields[0];
  1692. var variable = SaraUtils.createCatalogVariable(field);
  1693. gs.log("TQT:"+ field.name );
  1694. //new CatalogVariable(this.legitVarName(field.name), '9', 'why', '' , field.description, []);
  1695. variables.push(variable);
  1696. catItem.setVariables(variables);
  1697. return catItem;
  1698.  
  1699. }
  1700.  
  1701. /**
  1702. * Legitimates variable name that is not qualified business rule validate_variable_name
  1703. */
  1704. SaraUtils.legitVarName = function(varName) {
  1705. var firstLegal = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  1706. var firstLetter = varName.charAt(0);
  1707. if (firstLegal.indexOf(firstLetter) == -1) {
  1708. varName = "v_" + varName;
  1709. }
  1710. var legal = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789';
  1711. for (var i = 0; i < varName.length; i++) {
  1712. var letter = varName.charAt(i);
  1713. if (legal.indexOf(letter) == -1) {
  1714. // replace illegal char with _
  1715. varName = varName.substr(0, i) + '_' + varName.substr(i + 1);
  1716. }
  1717. }
  1718. return varName;
  1719. }
  1720.  
  1721. /**
  1722. * Builds url with respect to configurable 'ref_scheme' property that might be 'relative' or 'absolute'
  1723. */
  1724. SaraUtils.buildUrl = function(url) {
  1725. if (!url) url = '';
  1726. if (SaraCommon.getRefScheme() === 'relative') {
  1727. var basePath = SaraCommon.getBasePath();
  1728. if (basePath.charAt(basePath.length - 1) == '/' && url.substring(0,1) == '/') {
  1729. return basePath + url.substring(1);
  1730. }
  1731. if (basePath.charAt(basePath.length - 1) != '/' && url.substring(0,1) != '/') {
  1732. return basePath + '/' + url;
  1733. }
  1734. return basePath + url;
  1735. }
  1736. if (url == '/') return SaraCommon.getBasePath();
  1737. return url;
  1738. }
  1739. ]]></content>
  1740. <description>Sara Utilities</description>
  1741. <active>true</active>
  1742. <client_callable>false</client_callable>
  1743. </script_include>
  1744. <script_include>
  1745. <name>SAPIConsumer</name>
  1746. <content><![CDATA[gs.include('SaraModel');
  1747. gs.include('SaraCatalogGenerator');
  1748. gs.include('SaraRestClient');
  1749. gs.include('SaraUtils');
  1750. gs.include('SaraCommon');
  1751.  
  1752. var SAPIConsumer= Class.create();
  1753. SAPIConsumer.prototype = {
  1754. initialize: function() {
  1755. // initialize dependencies
  1756.  
  1757. var midserver = SaraCommon.getMidServer();
  1758. this.restClient = new SaraRestClient();
  1759. this.restClient.setMIDServer(midserver);
  1760.  
  1761. this.username = gs.getUserName();
  1762.  
  1763. this.statusCode = '';
  1764. this.errorMessage = '';
  1765.  
  1766. // inject restClient to the OAuth client instance
  1767. this.oauthClient = new SaraOAuthClient(this.restClient);
  1768. this.tokens = this.oauthClient.getUserTokens(this.username);
  1769.  
  1770.  
  1771. this.generator = new SaraCatalogGenerator();
  1772. // list of catalog category
  1773. this.catalogCategoryEntities = [];
  1774. // list of catalog item
  1775. this.catalogItemEntities = [];
  1776. // item category table;
  1777. this.categorySysId = {};
  1778. },
  1779.  
  1780. restClient: null,
  1781.  
  1782. // private method to check user permission.
  1783. _checkPermission: function(){
  1784. if (!gs.hasRole("admin") && !gs.hasRole("groups_admin")) {
  1785. gs.log("You don't have sufficient permission to do this action. Allowed roles: admin, groups_admin",'SARA');
  1786. return null;
  1787. }
  1788. // check user tokens
  1789. if (!this.tokens) {
  1790. gs.log('No tokens found for user ' + this.username + '. Skip import','SARA');
  1791. return null;
  1792. }
  1793.  
  1794. gs.log('Tokens found for user ' + this.username,'SARA');
  1795. if (!this.tokens[3]) this.tokens[3] = 'bearer';
  1796. },
  1797.  
  1798. /**
  1799. * discover SAPI consumer URL, create corresponding list of Catalog Category and Catalog Item
  1800. */
  1801. discover: function() {
  1802. this._checkPermission();
  1803.  
  1804. /*************************************************************************************************
  1805. * Block request Sapi REST API: Root URL *
  1806. **************************************************************************************************/
  1807. // Make request
  1808. this.tokens = this.oauthClient.getUserTokens(this.username);
  1809. var requestUrl = SaraUtils.buildUrl('/');
  1810. //var request = this.restClient.newEntryRequest();
  1811. var request = this.restClient.newServiceRequest('Entry','get',requestUrl);
  1812. request.addHeader('Authorization', this.tokens[3] + ' ' + this.tokens[0]);
  1813. // Get response
  1814. var response = this.restClient.executeWithRefreshToken(request);
  1815. if (!response || !response.getStatusCode() || response.getStatusCode() < 200 || response.getStatusCode() > 299) return response;
  1816.  
  1817. // Parsing return Objects
  1818. var resObj = (new JSONParser()).parse(response.getBody());
  1819. /*************************************************************************************************/
  1820.  
  1821. if (!resObj["_links"] || !resObj["_links"]["catalogs"]){
  1822. gs.log('No catalog consumer URL found, skip discover!','SARA');
  1823. return null;
  1824. }
  1825.  
  1826. /*************************************************************************************************
  1827. * Block request Sapi REST API: /catalogs URL *
  1828. **************************************************************************************************/
  1829. // Make request
  1830. this.tokens = this.oauthClient.getUserTokens(this.username);
  1831. var requestUrl = SaraUtils.buildUrl(resObj["_links"]["catalogs"]["href"]);
  1832. request = this.restClient.newServiceRequest('Sapi','get',requestUrl);
  1833. request.addHeader('Authorization', this.tokens[3] + ' ' + this.tokens[0]);
  1834.  
  1835. // Get response
  1836. response = this.restClient.executeWithRefreshToken(request);
  1837. if (!response || response.getStatusCode() < 200 || response.getStatusCode() > 299) return response;
  1838.  
  1839. // Parsing return Objects
  1840. var catalogsObj = (new JSONParser()).parse(response.getBody());
  1841. /*************************************************************************************************/
  1842.  
  1843. // TODO: Should change to catalogsObj["_links"]["catalog"]
  1844. if (!catalogsObj["_links"] || !catalogsObj["_links"]["catalog"]){
  1845. gs.log('No SAPI catalog found, skip discover!','SARA')
  1846. return null;
  1847. }
  1848.  
  1849. // discover SAPI Catalogs
  1850. // TODO: Only for case: there is 1 item in list catalog, the JSON list will return as an object, not an array.
  1851. // Should remove when all return are in array type.
  1852. gs.log('Found '+catalogsObj["count"]+' SAPI Catalogs, start discover each catalog.', 'SARA');
  1853.  
  1854. // In case there is only 1 catalog, convert catalogsObj["_links"]["catalog"] object to array with 1 element.
  1855. if (catalogsObj["count"]==1 || catalogsObj["count"]=="1"){
  1856. catalogsObj["_links"]["catalog"]=[catalogsObj["_links"]["catalog"]];
  1857. }
  1858. // TODO: For common case: there are more than 1 item in list catalog, JSON list return an array of catalog.
  1859. for (var i in catalogsObj["_links"]["catalog"]){
  1860. var catalogEntity = catalogsObj["_links"]["catalog"][i];
  1861. if (!catalogEntity.href){
  1862. break;
  1863. }
  1864.  
  1865. gs.log('Found catalog "'+catalogEntity.title+'" : ' + catalogEntity.href ,'SARA');
  1866. this.tokens = this.oauthClient.getUserTokens(this.username);
  1867. requestUrl = SaraUtils.buildUrl(catalogEntity.href);
  1868. request = this.restClient.newServiceRequest('Sapi','get',requestUrl);
  1869. request.addHeader('Authorization', this.tokens[3] + ' ' + this.tokens[0]);
  1870.  
  1871. response = this.restClient.executeWithRefreshToken(request);
  1872. if (!response || !response.getStatusCode() || response.getStatusCode() < 200 || response.getStatusCode() > 299) return response;
  1873.  
  1874. var catObj = (new JSONParser()).parse(response.getBody());
  1875.  
  1876. // store CatalogCategoryEntity to create later.
  1877. var catName = (catalogEntity.title?catalogEntity.title:catObj.name);
  1878. var catalogCategoryEntity = new CatalogCategory(catName,catObj.name, '', true, catObj.shortDescription);
  1879. gs.log('Found catalog category at: '+catObj["_links"]["self"]["href"],'SARA');
  1880. if (catObj["_links"]["self"]["href"])
  1881. catalogCategoryEntity.setConsumeUrl(catObj["_links"]["self"]["href"]);
  1882. this.catalogCategoryEntities.push(catalogCategoryEntity);
  1883.  
  1884. if (catObj["_links"] && catObj["_links"]["service"]){
  1885. // In case there is only 1 service, convert catObj["_links"]["service"] object to array with 1 element.
  1886. if (catObj.count==1 || catObj.count=="1"){
  1887. catObj["_links"]["service"] = [catObj["_links"]["service"]];
  1888. }
  1889. // discover Services inside each SAPI Catalog
  1890. for (var j in catObj["_links"]["service"]){
  1891. var serviceObj = catObj["_links"]["service"][j];
  1892. if (serviceObj.href){
  1893. var catalogItemEntity = new CatalogItem(serviceObj.title,catObj.name,true,'','','',null,null);
  1894. catalogItemEntity.setConsumeUrl(serviceObj.href);
  1895. gs.log('Found service "'+serviceObj.title+'" inside catalog "'+catObj.name+'"','SARA');
  1896. this.catalogItemEntities.push(catalogItemEntity);
  1897. }
  1898. }
  1899. }
  1900. }
  1901. return response;
  1902. },
  1903.  
  1904. /** Imports with regard to update behaviour */
  1905. discoverItem : function (catalogItemEntities) {
  1906. var catalogItems = [];
  1907. var response = null;
  1908. for (var i in catalogItemEntities) {
  1909. var item = catalogItemEntities[i];
  1910.  
  1911. // Make request to catalog item details
  1912. this.tokens = this.oauthClient.getUserTokens(this.username);
  1913. var requestUrl = SaraUtils.buildUrl(item.getConsumeUrl());
  1914. var request = this.restClient.newServiceRequest('Sapi', 'get', requestUrl);
  1915. request.addHeader('Authorization', this.tokens[3] + ' ' + this.tokens[0]);
  1916.  
  1917. response = this.restClient.executeWithRefreshToken(request);
  1918. if (!response || !response.getStatusCode() || response.getStatusCode() < 200 || response.getStatusCode() > 299) {
  1919. gs.log('Request to ' + requestUrl + ' failed', 'SARA');
  1920. if (response) {
  1921. gs.log("Status code: " + response.getStatusCode() + ", error message: " + response.getBody(), 'SARA');
  1922. }
  1923. return;
  1924. }
  1925. var service = (new JSONParser()).parse(response.getBody());
  1926.  
  1927. var variables = [];
  1928.  
  1929. //
  1930. var itemName = (item.name ? item.name : service.name);
  1931. gs.log('Set category for Item ' + item.getCategory() + ' to ' + this.categorySysId[item.getCategory()].sysId, 'SARA');
  1932. var catItem = new CatalogItem(itemName, this.categorySysId[item.getCategory()].sysId, true, 'Sara Service', service.shortDescription, service.shortDescription, [], '');
  1933.  
  1934. gs.log('SERVICE NAME = ' + service.name, 'SARA');
  1935.  
  1936. catItem.setServiceName(service.name);
  1937. catItem.setConsumeUrl(service._links.consume.href);
  1938. catItem.isNew = this.categorySysId[item.getCategory()].isNew;
  1939. // In case there is only 1 form, convert service.forms object to array with 1 element.
  1940. if (service.forms.name) {
  1941. service.forms = [service.forms];
  1942. }
  1943. for (var i = 0; i < service.forms.length; i++) {
  1944. var j = 0;
  1945. var form = service.forms[i];
  1946. // set SARA item to use 'sara_cart' cart
  1947. catItem.setCustomCart('sara_cart');
  1948. // In case there is only 1 field, convert form.fields object to array with 1 element.
  1949. if (form.fields.type) {
  1950. form.fields = [form.fields];
  1951. }
  1952. for (j = 0; j < form.fields.length; j++) {
  1953. var field = form.fields[j];
  1954. var variable = SaraUtils.createCatalogVariable(field);
  1955. variables.push(variable);
  1956. }
  1957. }
  1958. catItem.setVariables(variables);
  1959. catalogItems.push(catItem);
  1960. }
  1961. this.catalogItemEntities = catalogItems.slice(0);
  1962. return response;
  1963. },
  1964.  
  1965. importItem: function(catalogItemEntities){
  1966. var glideHelper = new SaraGlideHelper();
  1967.  
  1968. for(var i in catalogItemEntities) {
  1969. var item = catalogItemEntities[i];
  1970. if (item.isNew) {
  1971. this.generator.save(item);
  1972. gs.log('Imported new item, name = ' + item.name + ', service name = ' + item.getServiceName(), 'SARA');
  1973. continue;
  1974. }
  1975.  
  1976. // check existing item with same name and same parent category id = item.category
  1977. var saraItems = glideHelper.getData('u_sara_definition', {'u_name': item.getServiceName(), 'u_parent': item.category, 'u_type': 'service'}, ['u_sc_item']);
  1978. if (saraItems && saraItems.length > 0) {
  1979. // update sc item with sys_id = saraItems[0].u_sc_item
  1980. item.sysId = saraItems[0].u_sc_item;
  1981. this.generator.updateItem(item);
  1982. gs.log('Updated catalog item sys_id = ' + item.sysId + ', name = ' + item.name + ', service name = ' + item.getServiceName(), 'SARA');
  1983. } else {
  1984. // insert new as normal
  1985. this.generator.save(item);
  1986. gs.log('Imported new item, name = ' + item.name + ', service name = ' + item.getServiceName(), 'SARA');
  1987. }
  1988. }
  1989. },
  1990.  
  1991. importCategory: function(catalogs){
  1992. var targetCatId; // = '506d3e5a600a5980e73c9affd8c630e0';
  1993. var glideHelper = new SaraGlideHelper();
  1994.  
  1995. if (typeof wizard != 'undefined' && wizard && wizard.sara_target_category){
  1996. gs.log('Target category = \'' + wizard.sara_target_category + '\' ...', 'SARA');
  1997. targetCatId = wizard.sara_target_category;
  1998. }
  1999.  
  2000. // if there's no target category specified, all catalogs will be imported as new ones
  2001. if (!targetCatId) {
  2002. for (i in catalogs){
  2003. var catId = this.generator.saveCategory(catalogs[i]);
  2004. this.categorySysId[catalogs[i].name] = {'sysId': catId, 'isNew': true};
  2005. gs.log('Imported new category ' + catalogs[i].name + ' with sys_id = ' + catId, 'SARA');
  2006. }
  2007. return;
  2008. }
  2009.  
  2010. // otherwise, check if there's same name category existing under target category
  2011. for (i in catalogs) {
  2012. var catId, isNew = true;
  2013. var catalog = catalogs[i];
  2014. gs.log('Catalog ' + i + ': ' + new JSON().encode(catalog), 'SARA');
  2015.  
  2016. // check if there's same name category existing under target category
  2017. var saraCats = glideHelper.getData('u_sara_definition', {'u_name': catalog.name, 'u_parent': targetCatId, 'u_type': 'catalog'}, ['u_service_catalog_category']);
  2018. if (saraCats && saraCats.length > 0) {
  2019. isNew = false;
  2020. catId = saraCats[0]['u_service_catalog_category'];
  2021. }
  2022.  
  2023. catalog.isNew = isNew;
  2024.  
  2025. if (isNew) {
  2026. catId = this.generator.saveCategory(catalog);
  2027. this.categorySysId[catalogs[i].name] = {'sysId': catId, 'isNew': true};
  2028. gs.log('Imported new category ' + catalog.name + ' with sys_id = ' + catId, 'SARA');
  2029. } else {
  2030. // update category with sys_id = catId
  2031. catalog.sysId = catId;
  2032. this.generator.updateCategory(catalog);
  2033. this.categorySysId[catalogs[i].name] = {'sysId': catId, 'isNew': false};
  2034. gs.log('Updated category ' + catalog.name + ' with sys_id = ' + catId, 'SARA');
  2035. }
  2036.  
  2037. }
  2038. },
  2039.  
  2040. /**
  2041. * discover then import the data.
  2042. */
  2043. discoverAndImport: function(){
  2044. var response = this.discover();
  2045. if(response){
  2046. if (response.getStatusCode() && response.getBody() ){
  2047. if (response.getStatusCode()<200 || response.getStatusCode()>299){
  2048. this.statusCode = response.getStatusCode();
  2049. var resObj = new JSONParser().parse(response.getBody());
  2050. this.errorMessage = resObj.error + ' - ' + resObj.error_description;
  2051. return null;
  2052. }
  2053. } else {
  2054. this.statusCode = response.errorCode;
  2055. this.errorMessage = response.errorMessage;
  2056. gs.log('ERROR with statusCode = '+this.statusCode+' - ErrorMessage = '+this.errorMessage,'SARA');
  2057. return null;
  2058. }
  2059. }else{
  2060. this.statusCode = 0;
  2061. this.errorMessage = 'No response from server '+SaraCommon.getMidServer();
  2062. return null;
  2063. }
  2064. var logMsg = '';
  2065. if (this.catalogCategoryEntities){
  2066. this.importCategory(this.catalogCategoryEntities);
  2067. logMsg = logMsg.concat('\nImported '+this.catalogCategoryEntities.length+' Catalog categories!');
  2068. }
  2069. else gs.log('Cannot found any Catalog category!','SARA');
  2070.  
  2071.  
  2072. // Import service and finalize logMsg.
  2073. response = this.discoverItem(this.catalogItemEntities);
  2074. if(response){
  2075. if (response.getStatusCode() && response.getBody() ){
  2076. if (response.getStatusCode()<200 || response.getStatusCode()>299){
  2077. this.statusCode = response.getStatusCode();
  2078. var resObj = new JSONParser().parse(response.getBody());
  2079. this.errorMessage = resObj.error + ' - ' + resObj.error_description;
  2080. return null;
  2081. }
  2082. } else {
  2083. this.statusCode = response.errorCode;
  2084. this.errorMessage = response.errorMessage;
  2085. gs.log('ERROR with statusCode = '+this.statusCode+' - ErrorMessage = '+this.errorMessage,'SARA');
  2086. return null;
  2087. }
  2088. }else{
  2089. this.statusCode = 0;
  2090. this.errorMessage = 'No response from server '+SaraCommon.getMidServer();
  2091. return null;
  2092. }
  2093.  
  2094. this.importItem(this.catalogItemEntities);
  2095. logMsg = logMsg.concat('\nImported '+this.catalogItemEntities.length+' Catalog items!');
  2096.  
  2097.  
  2098. if (!this.catalogCategoryEntities && !this.catalogItemEntities)
  2099. return null;
  2100. else return logMsg;
  2101. },
  2102.  
  2103. getCatalogCategoryEntities: function(){
  2104. return this.catalogCategoryEntities;
  2105. },
  2106.  
  2107. getCatalogItemEntities: function(){
  2108. return this.catalogItemEntities;
  2109. },
  2110.  
  2111. getStatusCode: function(){
  2112. return this.statusCode;
  2113. },
  2114.  
  2115. getErrorMessage: function(){
  2116. return this.errorMessage;
  2117. },
  2118.  
  2119. type: 'SAPIConsumer'
  2120. }
  2121. ]]></content>
  2122. <description>SAPI consumer class to discover all SAPI catalogs, services and import them into SN as the corresponding catalog categories and items</description>
  2123. <active>true</active>
  2124. <client_callable>false</client_callable>
  2125. </script_include>
  2126. <script_include>
  2127. <name>SaraGlideHelper</name>
  2128. <content><![CDATA[/**
  2129. * Helper class provides convenient methods to work with ServiceNow data tables
  2130. */
  2131. var SaraGlideHelper = Class.create();
  2132. SaraGlideHelper.prototype = {
  2133.  
  2134. initialize : function () {},
  2135.  
  2136. /**
  2137. * Common data persistent operation
  2138. * @param table the table name
  2139. * @param entity object represents the row to insert
  2140. * @param referencing insert with references or not
  2141. * @return sys_id of inserted row
  2142. */
  2143. persistData : function (table, entity, referencing) {
  2144. var rec = new GlideRecord(table);
  2145. rec.initialize();
  2146. for (key in entity) {
  2147. // check if key contains 'sys_id'
  2148. var idx = key.indexOf('.sys_id');
  2149. if (idx > 0 && idx == key.length - 7) {
  2150. var propName = key.substring(0, idx);
  2151. rec[propName]['sys_id'] = entity[key];
  2152. } else rec[key] = entity[key];
  2153. }
  2154. if (referencing) {
  2155. return rec.insertWithReferences();
  2156. }
  2157. else {
  2158. return rec.insert();
  2159. }
  2160. },
  2161.  
  2162. /**
  2163. * Updates record(s) in given table based on the query and entity object holds fields/values to update
  2164. * @param table target table to update
  2165. * @param queryObj query object, property name is the field name to query, value is value to query
  2166. * @param entity holds fields and values to update
  2167. * @param updateAll update all matching record(s) or only the first one
  2168. * @param referencing update with references or not
  2169. */
  2170. updateData: function(table, queryObj, entity, updateAll, referencing) {
  2171. var rec = new GlideRecord(table);
  2172. if (queryObj) {
  2173. for (key in queryObj) {
  2174. rec.addQuery(key, queryObj[key]);
  2175. }
  2176. }
  2177. rec.query();
  2178.  
  2179. while (rec.next()) {
  2180. for (key in entity) {
  2181. // check if key contains 'sys_id'
  2182. var idx = key.indexOf('.sys_id');
  2183. if (idx > 0 && idx == key.length - 7) {
  2184. var propName = key.substring(0, idx);
  2185. rec[propName]['sys_id'] = entity[key];
  2186. } else rec[key] = entity[key];
  2187. }
  2188.  
  2189. if (referencing) {
  2190. rec.updateWithReferences();
  2191. } else {
  2192. rec.update();
  2193. }
  2194.  
  2195. if (!updateAll) break;
  2196. }
  2197.  
  2198. },
  2199.  
  2200. /**
  2201. * Retrieve sys_id of a record in given table by key=value query
  2202. * @param table the table name
  2203. * @param queryObj the object holds the query, property name is the field name to query, value is value to query
  2204. * @return sys_id that found, or null if no record found
  2205. */
  2206. getSysId : function (table, queryObj) {
  2207. var rec = new GlideRecord(table);
  2208. if (queryObj) {
  2209. for (key in queryObj) {
  2210. rec.addQuery(key, queryObj[key]);
  2211. }
  2212. }
  2213. rec.query();
  2214. if (rec.next()) {
  2215. return rec.sys_id;
  2216. }
  2217. else {
  2218. return null;
  2219. }
  2220. },
  2221.  
  2222. /**
  2223. * Query data from given table and query condition, return objects contain information specified in list of key
  2224. * @param table the target table
  2225. * @param queryObj the object holds the query, property name is the field name to query, value is value to query
  2226. * @param keys list of field members that will be included in result
  2227. * @return array of object with key from <code>keys</code>
  2228. */
  2229. getData: function(table, queryObj, keys) {
  2230. var rec = new GlideRecord(table);
  2231. var k;
  2232. if (queryObj) {
  2233. for (k in queryObj) {
  2234. rec.addQuery(k, queryObj[k]);
  2235. }
  2236. }
  2237. rec.query();
  2238. var result = [];
  2239. if (!keys) keys = ['sys_id'];
  2240.  
  2241. while (rec.next()) {
  2242. var obj = {};
  2243. for (idx in keys) {
  2244. obj[keys[idx]] = rec[keys[idx]].toString();
  2245. }
  2246. result.push(obj);
  2247. }
  2248. return result;
  2249. },
  2250.  
  2251. /**
  2252. * Delete record(s) from given table based on a query condition
  2253. * @param table the target table
  2254. * @param queryObj the object holds the query, property name is the field name to query, value is value to query
  2255. */
  2256. deleteData: function(table, queryObj, deleteAll) {
  2257. var rec = new GlideRecord(table);
  2258. if (queryObj) {
  2259. for (key in queryObj) {
  2260. rec.addQuery(key, queryObj[key]);
  2261. }
  2262. }
  2263. if (deleteAll) {
  2264. rec.deleteMultiple();
  2265. } else {
  2266. rec.query();
  2267. if (rec.next()) {
  2268. rec.deleteRecord();
  2269. }
  2270. }
  2271. },
  2272.  
  2273. type : 'SaraGlideHelper'
  2274. };
  2275. ]]></content>
  2276. <description>Helper class provides convenient methods to work with ServiceNow data tables</description>
  2277. <active>true</active>
  2278. <client_callable>false</client_callable>
  2279. </script_include>
  2280. </script_includes>
  2281.  
  2282.  
  2283. <ui_pages>
  2284. <ui_page>
  2285. <name>sara_oauth_authentication</name>
  2286. <description/>
  2287. <category>catalog</category>
  2288. <html><![CDATA[<g:ui_form>
  2289. <g:evaluate var="jvar_sysparm_id" expression="RP.getWindowProperties().get('sysparm_id')" />
  2290. <g:evaluate var="jvar_quantity" expression="RP.getWindowProperties().get('quantity')" />
  2291. <g:evaluate var="jvar_item_guid" expression="RP.getWindowProperties().get('item_guid')" />
  2292. <g:evaluate var="jvar_cart_action" expression="RP.getWindowProperties().get('cart_action')" />
  2293.  
  2294. <div class="sara_cart_hidden" style="display:none">
  2295. <input type="hidden" name="sara_order_sysparm_id" id="sara_order_sysparm_id" value="${jvar_sysparm_id}" />
  2296. <input type="hidden" name="sara_order_quantity" id="sara_order_quantity" value="${jvar_quantity}" />
  2297. <input type="hidden" name="sara_order_item_guid" id="sara_order_item_guid" value="${jvar_item_guid}" />
  2298. <input type="hidden" name="sara_cart_action" id="sara_cart_action" value="${jvar_cart_action}" />
  2299. </div>
  2300.  
  2301. <p><label>Username</label><input type="text" name="sara_oauth_username" id="sara_oauth_username" /></p>
  2302. <p><label>Password</label><input type="password" name="sara_oauth_password" id="sara_oauth_password" /></p>
  2303. <g:dialog_buttons_ok_cancel ok="validate(); return false;" cancel="processCancel(); return false;" />
  2304. <p id="sara_oauth_processing" style="display:none;"><label>Please wait, processing...</label></p>
  2305. </g:ui_form>
  2306. ]]></html>
  2307. <client_script><![CDATA[function validate() {
  2308. var username = trim(gel('sara_oauth_username').value);
  2309. var password = trim(gel('sara_oauth_password').value);
  2310. if (!username || !password) {
  2311. alert('Please provide username/password to authenticate against Automic OAuth Server.');
  2312. return false;
  2313. }
  2314.  
  2315. gel('sara_oauth_processing').style.display='block';
  2316.  
  2317. var sysparmId = gel('sara_order_sysparm_id').value;
  2318. var quantity = gel('sara_order_quantity').value;
  2319. var itemGuid = gel('sara_order_item_guid').value;
  2320. var cartAction = gel('sara_cart_action').value;
  2321.  
  2322. // invoke SaraOAuthAjax
  2323. var ga = new GlideAjax("SaraOAuthAjax");
  2324. ga.addParam('sysparm_name', 'requestToken');
  2325. ga.addParam('sysparm_username', username);
  2326. ga.addParam('sysparm_password', password);
  2327.  
  2328. //ga.getXMLWait();
  2329. //var answer = ga.getAnswer();
  2330. //console.log('Answer = ' + answer);
  2331. //if (answer == 'ok') {
  2332. // if (cartAction == "order") {
  2333. // var g_cart = new SCCart();
  2334. // g_cart.order(sysparmId, quantity, itemGuid);
  2335. // } else if (cartAction == "checkout") {
  2336. // // go to 'Order Status' page
  2337. // window.location='service_catalog.do?sysparm_action=checkout';
  2338. // }
  2339. //} else alert('Token request finished unsuccessfully' + answer?', error: ' + answer:'' + '. Please try again later or contact administrators.');
  2340.  
  2341. ga.getXML(function(response) {
  2342. var answer = response.responseXML.documentElement.getAttribute("answer");
  2343. console.log('Answer = ' + answer);
  2344. if (answer == 'ok') {
  2345. gel('sara_oauth_processing').childNodes[0].innerHTML = 'Token request successfully, redirecting to Order Status page...';
  2346. if (cartAction == "order") {
  2347. var g_cart = new SCCart();
  2348. g_cart.order(sysparmId, quantity, itemGuid);
  2349. } else if (cartAction == "checkout") {
  2350. // go to 'Order Status' page
  2351. window.location='service_catalog.do?sysparm_action=checkout';
  2352. }
  2353. } else {
  2354. alert('Token request finished unsuccessfully' + (answer?(', error: ' + answer):'') + '. Please try again later or contact administrators.');
  2355. GlideDialogWindow.get().destroy(); //Close the dialog window
  2356. }
  2357. });
  2358.  
  2359. return false;
  2360. }
  2361.  
  2362. function processCancel(){
  2363. GlideDialogWindow.get().destroy(); //Close the dialog window
  2364. //alert('Your order has been skipped.');
  2365. console.log('Order is skipped');
  2366. }
  2367. ]]></client_script>
  2368. <processing_script/>
  2369. </ui_page>
  2370. <ui_page>
  2371. <name>sara_servicecatalog_cart_view</name>
  2372. <description/>
  2373. <category>catalog</category>
  2374. <html><![CDATA[<?xml version="1.0" encoding="utf-8" ?>
  2375. <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
  2376. <j:set var="jvar_two_step" value="false" />
  2377. <g:macro_invoke macro="sara_servicecatalog_cart_template" />
  2378. <g:catalog_notify_cart_empty />
  2379. </j:jelly>
  2380. ]]></html>
  2381. </ui_page>
  2382. </ui_pages>
  2383.  
  2384.  
  2385. <ui_macros>
  2386. <ui_macro>
  2387. <name>sara_cart</name>
  2388. <description/>
  2389. <category>catalog</category>
  2390. <active>true</active>
  2391. <media_type/>
  2392. <xml><![CDATA[<?xml version="1.0" encoding="utf-8" ?>
  2393. <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
  2394.  
  2395. <g:set_if test="${sysparm_cartless}" var="jvar_cart_style" true="display:none" />
  2396. <table style="float: right">
  2397. <tr>
  2398. <td id="cart" style="${jvar_cart_style}"></td>
  2399. </tr>
  2400. </table>
  2401.  
  2402. <input type="hidden" id="sara_cart_indicator" value="This is the custom cart for SARA catalog items" />
  2403.  
  2404. <input type="hidden" id="sysparm_id" value="${sysparm_id}"/>
  2405. <table id="qty" style="display:none; width:100%" border="0">
  2406.  
  2407. <g:sc_cart_main />
  2408. <j:if test="${sysparm_cart_edit == null}">
  2409. <j:if test="${sc_cat_item.no_order != true}">
  2410. <j:if test="${sysparm_no_checkout != true}">
  2411. <j:if test="${sc_cat_item.no_order_now != true}">
  2412. <tr>
  2413. <td colspan="2" class="order_buttons">
  2414. <a class="request_catalog_button_with_icon" href="#" id="order_now" onclick="orderNow(); return false;" title="${gs.getMessage('Order Now')}">
  2415. <table>
  2416. <tr><td><img src="images/button_cursor.gifx" /></td><td class="text_cell">${gs.getMessage('Order Now')}</td></tr>
  2417. </table>
  2418. </a>
  2419. </td>
  2420. </tr>
  2421. </j:if>
  2422. </j:if>
  2423. <j:if test="${sc_cat_item.no_cart != true}">
  2424. <tr>
  2425. <td colspan="2" class="order_buttons">
  2426. <a class="request_catalog_button_with_icon" href="#" onclick="addToCart(); return false;" title="${gs.getMessage('Add to Cart')}">
  2427. <table><tr><td><img src="images/button_cart.gifx"/></td><td class="text_cell">${gs.getMessage('Add to Cart')}</td></tr></table>
  2428. </a>
  2429. </td>
  2430. </tr>
  2431. </j:if>
  2432. </j:if>
  2433. </j:if>
  2434. <j:if test="${sysparm_cart_edit != null}">
  2435. <tr>
  2436. <td colspan="2" class="order_buttons">
  2437. <a class="request_catalog_button_with_icon" href="#" onclick="orderEdit(); return false;" title="${gs.getMessage('Update Cart')}">
  2438. <table><tr><td><img src="images/button_cart.gifx"/></td><td class="text_cell">${gs.getMessage('Update Cart')}</td></tr></table>
  2439. </a>
  2440. </td>
  2441. </tr>
  2442. </j:if>
  2443. </table>
  2444.  
  2445. <script language="javascript">
  2446. var g_cart = null;
  2447.  
  2448. function scCartOnRender() {
  2449. g_cart = new SCCart();
  2450.  
  2451. <j:if test="${sc_cat_item.no_order != true}">
  2452. g_cart.attachWindow('qty', 'cart', "${gs.getMessage('Order this Item')}");
  2453. </j:if>
  2454. <j:if test="${sc_cat_item.no_cart == true}">
  2455. g_cart.setCartVisible(false);
  2456. </j:if>
  2457.  
  2458. g_cart.addCartContent();
  2459. g_cart.editID = '${sysparm_cart_edit}';
  2460. //g_cart.getWithBackButton();
  2461. var i = new CartItemList(g_cart);
  2462. i._afterChange = function() {
  2463. if (this.cart)
  2464. this.cart._changed();
  2465. _frameChanged();
  2466. modifyCartActions();
  2467. }
  2468. i.create();
  2469. i.get();
  2470. }
  2471.  
  2472. addRenderEvent(scCartOnRender);
  2473.  
  2474. function addToCart() {
  2475. var m = g_form.catalogOnSubmit();
  2476. if (!m) return;
  2477.  
  2478. var guid;
  2479. var item_guid = gel("sysparm_item_guid");
  2480. if (item_guid) guid = item_guid.value
  2481.  
  2482. // To prevent duplicate key violations due to multiple rapid clicks
  2483. // clear the item_guid if not empty and continue with the addToCart
  2484. // else return until a new item_guid is returned from the server
  2485. if (guid == "") return;
  2486.  
  2487. item_guid.value = "";
  2488.  
  2489. // hide the attachment header and delete out attachment name spans
  2490. var attachmentList = gel("header_attachment_list");
  2491. if (attachmentList) {
  2492. var count = attachmentList.childNodes.length;
  2493. while (count > 1) {
  2494. count--;
  2495. var node = attachmentList.childNodes[count];
  2496. rel(node);
  2497. }
  2498. var listLabel = gel("header_attachment_list_label");
  2499. listLabel.style.display = "none";
  2500.  
  2501. var spanNodes = $(listLabel).select("span");
  2502. if (spanNodes $[AMP]$[AMP] spanNodes.length != 0) spanNodes[0].update("");
  2503. }
  2504.  
  2505. g_cart.add(gel("sysparm_id").value, getQuantity(), guid);
  2506. }
  2507.  
  2508. function modifyCartActions() {
  2509. var edit = gel('catalog_cart_edit_button');
  2510. if (edit) {
  2511. edit.onclick = function() {
  2512. window.location='sara_servicecatalog_cart_view.do';
  2513. return false;
  2514. };
  2515. }
  2516.  
  2517. var checkout = gel('catalog_cart_proceed_checkout');
  2518. if (checkout) {
  2519. checkout.onclick = null;
  2520. checkout.onclick = function() { saraCheckout(); }
  2521. }
  2522.  
  2523. gel('sara_cart_indicator').value = gel('sara_cart_indicator').value + ', modified actions = true';
  2524. }
  2525.  
  2526. function saraCheckout() {
  2527.  
  2528. //gel("catalog_cart_proceed_checkout").onclick = "";
  2529.  
  2530. var currentUser = g_user.userName;
  2531. var rec = new GlideRecord('u_sara_oauth');
  2532. rec.addQuery('u_user.user_name', currentUser);
  2533. rec.query(function(rec) {
  2534. if (rec.next()) {
  2535. console.log('Token found: ' + rec.u_access_token);
  2536. // go to 'Order Status' page
  2537. window.location='service_catalog.do?sysparm_action=checkout';
  2538. } else {
  2539. console.log('Token not found, please provide your credentials to request tokens');
  2540. // pop out username/password dialog
  2541. var dialog = new GlideDialogWindow("sara_oauth_authentication");
  2542. dialog.setPreference("cart_action", "checkout");
  2543. dialog.setTitle("SARA OAuth Credentials");
  2544. dialog.removeCloseDecoration();
  2545. dialog.render();
  2546. return;
  2547. }
  2548. });
  2549. }
  2550.  
  2551. function orderNow() {
  2552. var m = g_form.catalogOnSubmit();
  2553. if (!m) return;
  2554.  
  2555. // Disable the Order Now button to prevent muliple item order
  2556. // as a result of muliple clicks before navigating away
  2557. //gel("order_now").onclick = "";
  2558.  
  2559. var item_guid = gel("sysparm_item_guid");
  2560. if (item_guid) item_guid = item_guid.value;
  2561.  
  2562. // check if current user has token or not
  2563. var currentUser = g_user.userName;
  2564. var rec = new GlideRecord('u_sara_oauth');
  2565. rec.addQuery('u_user.user_name', currentUser);
  2566. rec.query(function(rec) {
  2567. if (rec.next()) {
  2568. console.log('Token found: ' + rec.u_access_token);
  2569. g_cart.order(gel("sysparm_id").value, getQuantity(), item_guid);
  2570. } else {
  2571. console.log('Token not found, please provide your credentials to request tokens');
  2572.  
  2573. // pop out username/password dialog
  2574. var dialog = new GlideDialogWindow("sara_oauth_authentication");
  2575. dialog.setPreference("sysparm_id", gel("sysparm_id").value);
  2576. dialog.setPreference("quantity", getQuantity());
  2577. dialog.setPreference("item_guid", item_guid);
  2578. dialog.setPreference("cart_action", "order");
  2579. dialog.setTitle("SARA OAuth Credentials");
  2580. dialog.removeCloseDecoration();
  2581. dialog.render();
  2582.  
  2583. return;
  2584. }
  2585. });
  2586. }
  2587.  
  2588. function calcPrice() {
  2589. g_cart.recalcPrice(gel("sysparm_id").value, getQuantity());
  2590. }
  2591.  
  2592. function orderEdit(target) {
  2593. if (!target) target = '${sysparm_cart_edit}';
  2594.  
  2595. var m = g_form.catalogOnSubmit();
  2596. if (!m) return;
  2597.  
  2598. g_cart.edit(target, getQuantity());
  2599. }
  2600.  
  2601. // unused atm
  2602. function proceedCheckout(target) {
  2603. // check if current user has token or not
  2604. alert('In proceedCheckout');
  2605. var currentUser = g_user.userName;
  2606. var rec = new GlideRecord('u_sara_oauth');
  2607. rec.addQuery('u_user.user_name', currentUser);
  2608. rec.query(function(rec) {
  2609. if (rec.next()) {
  2610. alert('Token found: ' + rec.u_access_token);
  2611. var m = g_form.catalogOnSubmit();
  2612. if (!m) return;
  2613.  
  2614. g_cart.orderUpdate(target, getQuantity());
  2615. } else {
  2616. alert('Token not found');
  2617. var dialog = new GlideDialogWindow("sara_oauth_authentication"); //Render the dialog containing the UI Page 'task_comments_dialog'
  2618. dialog.setTitle("Request OAuth tokens"); //Set the dialog title
  2619. dialog.render(); //Open the dialog
  2620. return;
  2621. }
  2622. });
  2623. }
  2624.  
  2625. function getQuantity() {
  2626. var quantity = 1;
  2627. var quan_widget = gel("quantity");
  2628. if (quan_widget) quantity = quan_widget.value;
  2629. return quantity;
  2630. }
  2631. </script>
  2632. </j:jelly>
  2633. ]]></xml>
  2634. </ui_macro>
  2635. <ui_macro>
  2636. <name>sara_servicecatalog_cart_template</name>
  2637. <description/>
  2638. <category>catalog</category>
  2639. <active>true</active>
  2640. <media_type/>
  2641. <xml><![CDATA[<?xml version="1.0" encoding="utf-8" ?>
  2642. <j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
  2643. <g:include_script src="CartCheckoutFunctions.jsdbx"/>
  2644. <g:catalog_notify_cart_empty />
  2645.  
  2646. <!-- assumption is that a cart exists -->
  2647.  
  2648. <g2:evaluate var="jvar_item" expression="sc_cart = new GlideRecord('sc_cart'); sc_cart.get('user', system.getUserID()); var sc_cat_item = new GlideRecord('sc_cat_item'); sc_cat_item.initialize();"/>
  2649. <j:set var="jvar_checkout" value="checkout" />
  2650. <j:if test="${jvar_two_step == true}">
  2651. <j:set var="jvar_checkout" value="checkouttwo" />
  2652. </j:if>
  2653. <j:if test="${sysparm_guide == 'true'}">
  2654. <g:inline template="com.glideapp.servicecatalog_guide_banner.xml" />
  2655. </j:if>
  2656.  
  2657. <form method="POST" name="service_catalog.do" id="sc_cart.do">
  2658. <input type="HIDDEN" name="sysparm_view" value="${sysparm_view}" />
  2659. <input type="HIDDEN" name="sysparm_action" value="${jvar_checkout}" />
  2660. <input type="HIDDEN" name="sys_action" />
  2661. <table class="request_table" style="border-collapse: collapse;">
  2662. <tr class="header" border="0" cellspacing="0">
  2663. <td style="width: 22px;">
  2664. <img name="not_important" value="sysverb_back" id="sysverb_back" onClick="gel('sc_cart.do').sysparm_action.value ='sysverb_back';gel('sc_cart.do').submit();" src="images/green_back.gifx" title="${gs.getMessage('Back')}" alt="${gs.getMessage('Back')}" style="cursor:hand; margin-left: 4px;"/>
  2665. </td>
  2666. <td class="column_head" colspan="1">
  2667. <div class="caption">${gs.getMessage('Shopping Cart')}</div>
  2668. </td>
  2669. <td></td>
  2670. <td class="column_head" align="right" colspan="99">
  2671. <j:if test="${jvar_two_step != true}">
  2672. <button class="header" type="submit">
  2673. ${gs.getMessage('Check out')}
  2674. </button>
  2675. </j:if>
  2676. </td>
  2677. </tr>
  2678. </table>
  2679. <p class="order_details">
  2680. ${gs.getMessage('Are the contents of your cart correct? Please double check the items and remove and edit where appropriate')}
  2681. </p>
  2682. <table class="request_table" style="border-collapse: collapse;" border="0">
  2683. <j2:set var="jvar_never_show_price" value="$[gs.getProperty('glide.sc.price.display') == 'never']" />
  2684. <j2:set var="jvar_show_price" value="false" />
  2685. <j2:set var="jvar_got_one" value="false" />
  2686.  
  2687.  
  2688. <j2:set var="jvar_freq_list" value="$[SNC.CartBatcher.getFrequencyList(sc_cart.sys_id)]"/>
  2689. <j2:forEach var="jvar_freq" items="$[jvar_freq_list]">
  2690. <g2:evaluate var="freq_name" jelly="true">
  2691. var freq_name = jelly.jvar_freq;
  2692. freq_name;
  2693. </g2:evaluate>
  2694. <j2:set var="jvar_show_recurring_price_block" value="false" />
  2695. <j2:set var="jvar_show_price_block" value="false" />
  2696. <j2:set var="jvar_item_list" value="$[SNC.CartBatcher.getFrequencyItems(sc_cart.sys_id, freq_name)]"/>
  2697. <j2:set var="jvar_subtotal_block" value="$[0]" />
  2698. <j2:set var="jvar_recurring_subtotal_block" value="$[0]" />
  2699. <g2:evaluate var="jvar_line_num" expression="0" />
  2700. <j2:forEach var="jvar_item" items="$[jvar_item_list]">
  2701. <g2:evaluate var="sc_cart_item" jelly="true">
  2702. var sc_cart_item = new GlideRecord("sc_cart_item");
  2703. sc_cart_item.addQuery("sys_id", ""+jelly.jvar_item);
  2704. sc_cart_item.query();
  2705. sc_cart_item.next();
  2706. sc_cart_item;
  2707. </g2:evaluate>
  2708. <!-- jvar_freq = current_item.cat_item.name -->
  2709.  
  2710. <j2:set var="jvar_got_one" value="true" />
  2711. <g2:evaluate var="jvar_cat_item" expression="sc_cat_item = new GlideRecord('sc_cat_item'); sc_cat_item.addQuery('sys_id','$[sc_cart_item.cat_item]'); sc_cat_item.query(); sc_cat_item.next();"/>
  2712. <g2:evaluate var="cart_item_price">
  2713. var helper = new GlideappScriptHelper();
  2714. cart_item_price = helper.getCartItemPrice($[sc_cart_item]);
  2715. </g2:evaluate>
  2716. <g2:evaluate var="cart_item_recurring_price">
  2717. var helper = new GlideappScriptHelper();
  2718. cart_item_recurring_price = helper.getCartItemRecurringPrice($[sc_cart_item]);
  2719. </g2:evaluate>
  2720.  
  2721. <j2:if test="$[jvar_line_num == 0]">
  2722. <thead class="checkout_title">
  2723. <th class="checkout_left"/>
  2724. <j2:if test="$[freq_name != 'none']">
  2725. <th class="checkout_left">${gs.getMessage('Item')} (${gs.getMessage("Includes")} $[sc_cat_item.recurring_frequency.getDisplayValue()] ${gs.getMessage("Charges")})</th>
  2726. </j2:if>
  2727. <j2:if test="$[freq_name == 'none']">
  2728. <th class="checkout_left">${gs.getMessage('Item')}</th>
  2729. </j2:if>
  2730. <th class="checkout_left">${gs.getMessage('Delivery Time')}</th>
  2731. <j2:if test="$[!jvar_never_show_price]">
  2732. <th class="checkout_price">$[sc_cat_item.price.sys_meta.label] (ea.)</th>
  2733. </j2:if>
  2734. <th class="checkout_quantity">${gs.getMessage('Quantity')}</th>
  2735. <j2:if test="$[!jvar_never_show_price]">
  2736. <th class="checkout_total">${gs.getMessage('Total')}</th>
  2737. </j2:if>
  2738. </thead>
  2739. </j2:if>
  2740.  
  2741. <g2:inline template="com.glideapp.servicecatalog_show_price_function.xml" />
  2742. <j2:if test="$[show_price]">
  2743. <j2:set var="jvar_show_price" value="true" />
  2744. <j2:set var="jvar_show_price_block" value="true" />
  2745. </j2:if>
  2746. <j2:if test="$[show_recurring_price]">
  2747. <j2:set var="jvar_show_recurring_price_block" value="true" />
  2748. </j2:if>
  2749. <g2:evaluate var="jvar_line_num" expression="$[jvar_line_num] + 1" />
  2750. <g2:evaluate var="jvar_price">
  2751. this_price = '--';
  2752. if ($[show_price])
  2753. this_price = cart_item_price;
  2754. </g2:evaluate>
  2755. <g2:evaluate var="jvar_recurring_price">
  2756. this_recurring_price = '--';
  2757. if ($[show_recurring_price])
  2758. this_recurring_price = cart_item_recurring_price;
  2759. </g2:evaluate>
  2760. <g2:evaluate var="jvar_subtotal">
  2761. subtotal = 0;
  2762. if($[show_price]) {
  2763. if ($[this_price == '--'])
  2764. subtotal = 0;
  2765. else
  2766. subtotal = this_price * $[sc_cart_item.quantity];
  2767. } else
  2768. this_price;
  2769. </g2:evaluate>
  2770. <g2:evaluate var="jvar_recurring_subtotal">
  2771. recurring_subtotal = 0;
  2772. if($[show_recurring_price])
  2773. if ($[this_recurring_price == '--'])
  2774. recurring_subtotal = 0;
  2775. else
  2776. recurring_subtotal = this_recurring_price * $[sc_cart_item.quantity];
  2777. else
  2778. this_recurring_price;
  2779. </g2:evaluate>
  2780. <j2:if test="$[jvar_subtotal != '--']" >
  2781. <j2:set var="jvar_subtotal_block" value="$[jvar_subtotal_block + jvar_subtotal]" />
  2782. </j2:if>
  2783. <j2:if test="$[jvar_recurring_subtotal != '--']" >
  2784. <j2:set var="jvar_recurring_subtotal_block" value="$[jvar_recurring_subtotal_block + jvar_recurring_subtotal]" />
  2785. </j2:if>
  2786. <g2:evaluate var="jvar_total_price" expression="$[jvar_total_price] + subtotal"/>
  2787. <j2:set var="jvar_line_color" value="odd" />
  2788. <j2:if test="$[jvar_line_num % 2 == 0]" >
  2789. <j2:set var="jvar_line_color" value="even" />
  2790. </j2:if>
  2791. <tr class="$[jvar_line_color]">
  2792. <td class="checkout_left">
  2793. <a class="request_catalog_button" href="service_catalog.do?sysparm_action=cart_remove&amp;sysparm_id=$[sc_cart_item.sys_id]&amp;sysparm_ck=$[gs.getSessionToken()]">
  2794. ${gs.getMessage('Delete')}
  2795. </a>
  2796. <a class="request_catalog_button" href="com.glideapp.servicecatalog_cat_item_view.do?sysparm_view=${sysparm_view}&amp;sysparm_id=$[sc_cart_item.cat_item]&amp;sysparm_cart_edit=$[sc_cart_item.sys_id]&amp;sysparm_ck=$[gs.getSessionToken()]">
  2797. ${gs.getMessage('Edit')}
  2798. </a>
  2799. </td>
  2800. <td class="checkout_left" style="width: 40%;">
  2801. <table cellspacing="0" cellpadding="0" width="100%" class="background_transparent">
  2802. <g2:evaluate var="sc_cart_item_labels">
  2803. var sc_cart_item_labels = new CatalogLabelEvaluator(sc_cart_item).getLabels();
  2804. </g2:evaluate>
  2805. <g2:inline template="help_text.xml"
  2806. question_show_help="true"
  2807. question_help_tag="$[sc_cat_item.name.getDisplayValue() + ' - ' +sc_cat_item.short_description.getDisplayValue()]"
  2808. question_name="$[sc_cart_item.sys_id]"
  2809. help_class="$[jvar_line_color]">
  2810. <g:cart_variable_summary sc_cart_item_labels="$[sc_cart_item_labels]" />
  2811. </g2:inline>
  2812. </table>
  2813. </td>
  2814. <td class="checkout_left">
  2815. <j2:set var="jvar_wf" value="$[sc_cat_item.workflow]"/>
  2816. <j2:if test="$[!empty(jvar_wf)]">
  2817. <g2:evaluate var="jvar_wf_delivery_time" expression="new Workflow().getEstimatedDeliveryTime('$[jvar_wf]');" />
  2818. <j2:if test="$[!empty(jvar_wf_delivery_time)]">
  2819. $[jvar_wf_delivery_time]
  2820. </j2:if>
  2821. <j2:if test="$[empty(jvar_wf_delivery_time)]">
  2822. 0 $[gs.getMessage("Days")]
  2823. </j2:if>
  2824. </j2:if>
  2825. <j2:if test="$[empty(jvar_wf)]">
  2826. <j2:set var="jvar_dt" value="$[sc_cat_item.delivery_plan.total_delivery_time.getDisplayValue()]"/>
  2827. <j2:if test="$[!empty(jvar_dt)]">
  2828. $[jvar_dt]
  2829. </j2:if>
  2830. <j2:if test="$[empty(jvar_dt)]">
  2831. <g2:evaluate var="jvar_dt" jelly="true">
  2832. var jvar_dt = "";
  2833. var pgr = new GlideRecord("sc_cat_item_delivery_plan");
  2834. pgr.addQuery("name", "DEFAULT");
  2835. pgr.query();
  2836. if (pgr.next())
  2837. jvar_dt = pgr.total_delivery_time.getDisplayValue();
  2838. jvar_dt;
  2839. </g2:evaluate>
  2840. <j2:if test="$[!empty(jvar_dt)]">
  2841. $[jvar_dt]
  2842. </j2:if>
  2843. <j2:if test="$[empty(jvar_dt)]">
  2844. 0 $[gs.getMessage("Days")]
  2845. </j2:if>
  2846. </j2:if>
  2847. </j2:if>
  2848. </td>
  2849. <j2:if test="$[!jvar_never_show_price]">
  2850. <td class="checkout_price">
  2851. <j2:if test="$[show_price]">
  2852. <div><g2:currency_format double="$[this_price]"/></div>
  2853. </j2:if>
  2854. <j2:if test="$[!show_price]">
  2855. <div>-</div>
  2856. </j2:if>
  2857. <j2:if test="$[show_recurring_price]">
  2858. <j2:if test="$[!sc_cat_item.recurring_frequency.nil()]">
  2859. <div class="checkout_recurring_price">
  2860. + <g2:currency_format double="$[this_recurring_price]"/> $[sc_cat_item.recurring_frequency.getDisplayValue()]
  2861. </div>
  2862. </j2:if>
  2863. </j2:if>
  2864. </td>
  2865. </j2:if>
  2866. <td class="checkout_quantity">$[sc_cart_item.quantity]</td>
  2867. <j2:if test="$[!jvar_never_show_price]">
  2868. <td class="checkout_total">
  2869. <j2:if test="$[show_price]">
  2870. <div><g2:currency_format double="$[jvar_subtotal]"/></div>
  2871. </j2:if>
  2872. <j2:if test="$[!show_price]">
  2873. <div>-</div>
  2874. </j2:if>
  2875. <j2:if test="$[show_recurring_price]">
  2876. <j2:if test="$[!sc_cat_item.recurring_frequency.nil()]">
  2877. <div class="checkout_recurring_price_total">+ <g2:currency_format double="$[jvar_recurring_subtotal]"/> $[sc_cat_item.recurring_frequency.getDisplayValue()]</div>
  2878. </j2:if>
  2879. </j2:if>
  2880. </td>
  2881. </j2:if>
  2882. </tr>
  2883. </j2:forEach>
  2884. <j2:if test="$[!jvar_never_show_price]">
  2885. <thead class="checkout_title">
  2886. <j2:if test="$[jvar_never_show_price]">
  2887. <th style="font-size: 0px;" colspan="4">$[AMP]nbsp;</th>
  2888. </j2:if>
  2889. <j2:if test="$[!jvar_never_show_price]">
  2890. <th style="font-size: 0px;" colspan="6">$[AMP]nbsp;</th>
  2891. </j2:if>
  2892. </thead>
  2893.  
  2894. <tr style="border-bottom: 0px;">
  2895. <td/>
  2896. <td/>
  2897. <td/>
  2898. <td/>
  2899. <td class="checkoutTotalLabel">
  2900. <j2:if test="$[jvar_freq_list.length == 1]">
  2901. ${gs.getMessage('Total')}
  2902. </j2:if>
  2903. <j2:if test="$[jvar_freq_list.length != 1]">
  2904. ${gs.getMessage('Subtotal')}
  2905. </j2:if>
  2906. </td>
  2907. <td class="checkoutTotalSum" style="border-bottom: 1px solid #E0E0E0;">
  2908. <j2:if test="$[jvar_show_price_block]">
  2909. <div><g2:currency_format double="$[jvar_subtotal_block]"/></div>
  2910. </j2:if>
  2911. <j2:if test="$[!jvar_show_price_block]">
  2912. <div>-</div>
  2913. </j2:if>
  2914. <j2:if test="$[freq_name != 'none']">
  2915. <div class="checkout_recurring_price_subtotal">+ <g2:currency_format double="$[jvar_recurring_subtotal_block]"/> $[sc_cat_item.recurring_frequency.getDisplayValue()]</div>
  2916. </j2:if>
  2917. </td>
  2918. </tr>
  2919. </j2:if>
  2920. <tr>
  2921. <td colspan="4">$[AMP]nbsp;<br/></td>
  2922. <j2:if test="$[!jvar_never_show_price]">
  2923. <td colspan="2"/>
  2924. </j2:if>
  2925. </tr>
  2926. </j2:forEach>
  2927.  
  2928.  
  2929. <j2:if test="$[!jvar_never_show_price and jvar_freq_list.length != 1]">
  2930. <thead class="checkout_title">
  2931. <j2:if test="$[jvar_never_show_price]">
  2932. <th style="font-size: 0px;" colspan="4">$[AMP]nbsp;</th>
  2933. </j2:if>
  2934. <j2:if test="$[!jvar_never_show_price]">
  2935. <th style="font-size: 0px;" colspan="6">$[AMP]nbsp;</th>
  2936. </j2:if>
  2937. </thead>
  2938. <tr style="border-bottom: 0px;">
  2939. <td/>
  2940. <td/>
  2941. <td/>
  2942. <td/>
  2943. <td class="checkoutTotalLabel">${gs.getMessage('Total')}</td>
  2944. <td class="checkoutTotalSum" style="border-bottom: 1px solid #E0E0E0;">
  2945. <j2:if test="$[jvar_show_price]">
  2946. <div><g2:currency_format double="$[jvar_total_price]"/></div>
  2947. </j2:if>
  2948. <j2:if test="$[!jvar_show_price]">
  2949. <div>-</div>
  2950. </j2:if>
  2951. </td>
  2952. </tr>
  2953. <tr style="border-bottom: 0px;">
  2954. <td colspan="4">$[AMP]nbsp;<br/></td>
  2955. <j2:if test="$[!jvar_never_show_price]">
  2956. <td colspan="2"/>
  2957. </j2:if>
  2958. </tr>
  2959. </j2:if>
  2960. <j2:if test="$[jvar_got_one == false]">
  2961. <tr>
  2962. <td>
  2963. <p class="order_details">${gs.getMessage('Cart is empty')}</p>
  2964. </td>
  2965. </tr>
  2966. </j2:if>
  2967. </table>
  2968.  
  2969.  
  2970.  
  2971. <j:if test="${jvar_two_step == true}">
  2972. <p class="order_details">
  2973. ${gs.getMessage('If this request is for someone other than yourself please provide detailed information in the fields provided below.')}
  2974. </p>
  2975. <table class="wide" cellspacing="0" cellpadding="3">
  2976. <j:set var="ref" value="sc_cart" />
  2977. <j:set var="jvar_skip_attachment_footer" value="true" />
  2978. <j2:set var="jvar_ref" value="$[sc_cart.requested_for]"/>
  2979. <j2:set var="jvar_ref_display" value="$[sc_cart.requested_for.getDisplayValue()]" />
  2980. <g2:evaluate var="jvar_can_delta_rf" expression="var helper = new GlideappCalculationHelper(); helper.canViewRF();"/>
  2981. <tr class="header">
  2982. <td width="30%" >
  2983. ${gs.getMessage('Requested for')}:
  2984. </td>
  2985. <td width="70%">
  2986. <label for="requestor_location">${gs.getMessage('Deliver to')}:</label>
  2987. </td>
  2988. </tr>
  2989. <tr>
  2990. <td>
  2991. $[SP]
  2992. </td>
  2993. </tr>
  2994. <tr>
  2995. <td valign="top">
  2996. <j2:if test="$[jvar_can_delta_rf == false]">
  2997. $[sc_cart.requested_for.getDisplayValue()]
  2998. </j2:if>
  2999. <j2:if test="$[jvar_can_delta_rf != false]">
  3000. <g2:catalog_requested_for />
  3001. </j2:if>
  3002. </td>
  3003. <td>
  3004. <g2:evaluate var="jvar_set_address" jelly="true">
  3005. var jvar_set_address = jelly.jvar_can_delta_rf == "false" $[AMP]$[AMP] (sc_cart.delivery_address == null || sc_cart.delivery_address == "");
  3006. jvar_set_address;
  3007. </g2:evaluate>
  3008. <j2:if test="$[jvar_set_address]">
  3009. <g2:evaluate var="calculated_address">
  3010. function addValue(value, extra, cond) {
  3011. var returnValue = '';
  3012. if (value != '') {
  3013. returnValue += value;
  3014. if (cond)
  3015. returnValue += extra;
  3016. }
  3017. return returnValue;
  3018. }
  3019. function generateLocation(loc) {
  3020. var address = '';
  3021. var streetOK = loc.street != null $[AMP]$[AMP] loc.street != '';
  3022. var cityOK = loc.city != null $[AMP]$[AMP] loc.city != '';
  3023. var stateOK = loc.state != null $[AMP]$[AMP] loc.state != '';
  3024. var zipOK = loc.zip != null $[AMP]$[AMP] loc.zip != '';
  3025. if (streetOK || cityOK || stateOK || zipOK) {
  3026. address += addValue(loc.street, '\n', cityOK || stateOK || zipOK);
  3027. address += addValue(loc.city, ', ', stateOK || zipOK);
  3028. address += addValue(loc.state, ', ', zipOK);
  3029. address += addValue(loc.zip, ', ', true);
  3030. }
  3031. return address;
  3032. }
  3033. var calculated_address = '';
  3034. var gr = new GlideRecord('sys_user');
  3035. gr.addQuery('sys_id', sc_cart.requested_for);
  3036. gr.query();
  3037. if (gr.next()) {
  3038. calculated_address = generateLocation(gr);
  3039. if (calculated_address == null || calculated_address == '') {
  3040. var loc = new GlideRecord('cmn_location');
  3041. loc.addQuery('sys_id', gr.location);
  3042. loc.query();
  3043. if (loc.next())
  3044. calculated_address = generateLocation(loc);
  3045. }
  3046. }
  3047. calculated_address;
  3048. </g2:evaluate>
  3049. </j2:if>
  3050. <textarea id="requestor_location" style="width: 100%" rows="4" name="requestor_location" wrap="soft" onChange="catDeliveryAddress('$[sc_cart.sys_id]', 'requestor_location');">
  3051. <j2:if test="$[jvar_set_address]">
  3052. $[calculated_address]
  3053. </j2:if>
  3054. <j2:if test="$[!jvar_set_address]">
  3055. $[sc_cart.delivery_address]
  3056. </j2:if>
  3057. </textarea>
  3058. </td>
  3059. </tr>
  3060. <tr>
  3061. <td>$[SP]</td>
  3062. </tr>
  3063. <tr class="header" >
  3064. <td>
  3065. <label for="special_instructions">${gs.getMessage('Special instructions')}</label>
  3066. </td>
  3067. <td align="right">${gs.getMessage('Add attachment...')}
  3068. <a onclick="saveCartAttachment('$[sc_cart.sys_id]');">
  3069. <img src="images/icons/attachment.gifx" width="16" height="16"
  3070. border="0" alt="${gs.getMessage('Attachments')}" title="${gs.getMessage('Attachments')}"/>
  3071. </a>
  3072. </td>
  3073. </tr>
  3074. <tr>
  3075. <td colspan="2">
  3076. <g:inline template="attachment_list.xml" />
  3077. </td>
  3078. </tr>
  3079. <tr>
  3080. <td colspan="2">
  3081. <textarea id="special_instructions" style="width: 100%" rows="6"
  3082. name="special_instructions" wrap="soft"
  3083. onChange="catSpecialInstructions('$[sc_cart.sys_id]', 'special_instructions');" >
  3084. $[sc_cart.special_instructions]
  3085. </textarea>
  3086. </td>
  3087. </tr>
  3088. </table>
  3089. <table class="wide" cellspacing="0">
  3090. <j:if test="${jvar_suppress_buttonbar != 'true'}">
  3091. <tr>
  3092.  
  3093. <td align="left">
  3094. <j2:set var="jvar_show_back" value="$[gs.getProperty('glide.sc.checkout.twostep.back')]" />
  3095. <j2:if test="$[jvar_show_back == 'true']">
  3096. <j2:if test="$[sysparm_guide != 'true']">
  3097. <a href="#" class="request_catalog_button" onclick="window.location='catalog_home.do?sysparm_view=catalog_default'; return false;">
  3098. ${gs.getMessage('Back to Catalog');}
  3099. </a>
  3100. </j2:if>
  3101. <j2:if test="$[sysparm_guide == 'true']">
  3102. <a href="#" class="request_catalog_button" onclick="window.location='service_catalog.do?sysparm_action=restart_guide&amp;sysparm_ck=$[gs.getSessionToken()]'; return false;">
  3103. ${gs.getMessage('Choose Options');}
  3104. </a>
  3105. </j2:if>
  3106. </j2:if>
  3107. </td>
  3108. <j2:if test="$[sysparm_guide != 'true']">
  3109. <td style="width: 100%">
  3110. </td>
  3111. </j2:if>
  3112. <j2:if test="$[sysparm_guide == 'true']">
  3113. <g2:evaluate var="not_important" expression="
  3114. var returnToCatalogHome = 'false';
  3115. var guide = '$[sysparm_order_guide]';
  3116. var og = new GlideappOrderGuide(guide);
  3117. if (og.isOrderToCart())
  3118. returnToCatalogHome = 'true';
  3119. "/>
  3120. <j2:if test="$[returnToCatalogHome != 'true']">
  3121. <td style="width: 100%">
  3122. </td>
  3123. </j2:if>
  3124. <j2:if test="$[returnToCatalogHome == 'true']">
  3125. <td style="width: 50%">
  3126. </td>
  3127. <td align="center">
  3128. <a href="#" class="request_catalog_button"
  3129. onclick="window.location='catalog_home.do?sysparm_view=catalog_default'; return false;">
  3130. ${gs.getMessage('Back to Catalog');}
  3131. </a>
  3132. </td>
  3133. <td style="width: 50%">
  3134. </td>
  3135. </j2:if>
  3136. </j2:if>
  3137. <td align="right">
  3138. <a href="#" class="request_catalog_button" type="submit" value="sysverb_insert" onClick="return gsftSubmit(this);" id="sysverb_insert">
  3139. ${gs.getMessage('Submit Order');}
  3140. </a>
  3141. </td>
  3142. </tr>
  3143. </j:if>
  3144. </table>
  3145. <j2:set var="jvar_address" value="$[sc_cart.delivery_address]"/>
  3146. <j2:set var="jvar_p2flow_all_visible_fields" value="requested_for" />
  3147. <g:client_script tableName="sc_cart" type="phase1"/>
  3148. <g2:client_script tableName="sc_cart" file="$[sc_cart]"/>
  3149. </j:if>
  3150. </form>
  3151. <script language="javascript">
  3152. gel('sc_cart.do').onsubmit = function(e) {
  3153. saraCheckout();
  3154. e.preventDefault();
  3155. return false;
  3156. };
  3157.  
  3158. function saraCheckout() {
  3159. //
  3160. var currentUser = "${gs.getUserName()}";
  3161. console.log('Current user is ' + currentUser);
  3162.  
  3163. var rec = new GlideRecord('u_sara_oauth');
  3164. rec.addQuery('u_user.user_name', currentUser);
  3165. rec.query(function(rec) {
  3166. if (rec.next()) {
  3167. console.log('Token found: ' + rec.u_access_token);
  3168. // go to 'Order Status' page
  3169. window.location='service_catalog.do?sysparm_action=checkout';
  3170. } else {
  3171. console.log('Token not found, please provide your credentials to request tokens');
  3172. // pop out username/password dialog
  3173. var dialog = new GlideDialogWindow("sara_oauth_authentication");
  3174. dialog.setPreference("cart_action", "checkout");
  3175. dialog.setTitle("SARA OAuth Credentials");
  3176. dialog.removeCloseDecoration();
  3177. dialog.render();
  3178. return;
  3179. }
  3180. });
  3181. return false;
  3182. }
  3183. </script>
  3184. </j:jelly>
  3185. ]]></xml>
  3186. </ui_macro>
  3187. </ui_macros>
  3188.  
  3189.  
  3190. <workflows>
  3191. <workflow>
  3192. <name>SaraService</name>
  3193. <description>Sara Service workflow</description>
  3194. <start/>
  3195. <table>sc_req_item</table>
  3196. <active>true</active>
  3197. <published>true</published>
  3198. <stages>
  3199. <stage>
  3200. <name>Initializing</name>
  3201. <order>50</order>
  3202. <value>initializing</value>
  3203. <ola/>
  3204. </stage>
  3205. <stage>
  3206. <name>Transfered</name>
  3207. <order>100</order>
  3208. <value>transfered</value>
  3209. <ola/>
  3210. </stage>
  3211. <stage>
  3212. <name>Active</name>
  3213. <order>200</order>
  3214. <value>active</value>
  3215. <ola/>
  3216. </stage>
  3217. <stage>
  3218. <name>Transfer Failed</name>
  3219. <order>300</order>
  3220. <value>transfer_failed</value>
  3221. <ola/>
  3222. </stage>
  3223. <stage>
  3224. <name>Ended OK</name>
  3225. <order>300</order>
  3226. <value>ended_ok</value>
  3227. <ola/>
  3228. </stage>
  3229. <stage>
  3230. <name>Ended Failed</name>
  3231. <order>300</order>
  3232. <value>ended_failed</value>
  3233. <ola/>
  3234. </stage>
  3235. <stage>
  3236. <name>Completed</name>
  3237. <order>1000</order>
  3238. <value>completed</value>
  3239. <ola/>
  3240. </stage>
  3241. </stages>
  3242. <activities>
  3243. <activity>
  3244. <name>Begin</name>
  3245. <activity_definition>Begin</activity_definition>
  3246. <stage/>
  3247. <width>80</width>
  3248. <height/>
  3249. <x>20</x>
  3250. <y>20</y>
  3251. <snsc_name>begin</snsc_name>
  3252. </activity>
  3253. <activity>
  3254. <name>Initialize</name>
  3255. <activity_definition>Timer</activity_definition>
  3256. <stage>Initializing</stage>
  3257. <width/>
  3258. <height/>
  3259. <x>70</x>
  3260. <y>170</y>
  3261. <snsc_name>initialize</snsc_name>
  3262. <variables>
  3263. <variable>
  3264. <name>timer_type</name>
  3265. <value>script</value>
  3266. </variable>
  3267. <variable>
  3268. <name>script</name>
  3269. <value><![CDATA[workflow.debug('Initializing ...');
  3270.  
  3271. workflow.debug('Workflow started by ' + current.opened_by.user_name);
  3272. workflow.scratchpad.openedBy = current.opened_by.user_name;
  3273.  
  3274. // Set 'answer' to the number of seconds this timer should wait
  3275. answer = 2;
  3276. ]]></value>
  3277. </variable>
  3278. </variables>
  3279. </activity>
  3280. <activity>
  3281. <name>Invoke Consume URL</name>
  3282. <activity_definition>Run Script</activity_definition>
  3283. <stage>Transfered</stage>
  3284. <width/>
  3285. <height/>
  3286. <x>200</x>
  3287. <y>310</y>
  3288. <snsc_name>invoke_consume</snsc_name>
  3289. <variables>
  3290. <variable>
  3291. <name>script</name>
  3292. <value><![CDATA[var restClient = new SaraRestClient();
  3293. restClient.setMIDServer(SaraCommon.getMidServer());
  3294. var oauthClient = new SaraOAuthClient(restClient);
  3295.  
  3296. // get tokens, store tokens in workflow scratchpad
  3297. var tokens = oauthClient.getUserTokens(workflow.scratchpad.openedBy);
  3298.  
  3299. if (!tokens) {
  3300. workflow.debug("Cannot find access tokens for this user '" + workflow.scratchpad.openedBy + "'. Order will be skipped");
  3301. workflow.cancel(current);
  3302. }
  3303.  
  3304. workflow.scratchpad.tokens = { 'accessToken': tokens[0], 'refreshToken': tokens[1], 'tokenType': tokens[3] };
  3305.  
  3306. // get service name & consume URL
  3307. var serviceName, consumeUrl, varSetId;
  3308. var catId = current.cat_item.sys_id;
  3309. var rec = new GlideRecord('u_sara_definition');
  3310. rec.addQuery('u_sc_item', catId);
  3311. rec.query();
  3312. if (rec.next()) {
  3313. serviceName = rec.u_name;
  3314. consumeUrl = rec.u_consume_url;
  3315. varSetId = rec.u_sara_var_set.sys_id;
  3316. }
  3317.  
  3318. consumeUrl = SaraUtils.buildUrl(consumeUrl);
  3319.  
  3320. workflow.debug('Service name = ' + serviceName);
  3321. workflow.debug('Consume URL = ' + consumeUrl);
  3322.  
  3323. workflow.debug('Cat item: ' + current.cat_item.name);
  3324.  
  3325. var payload = {};
  3326. var payloadFields = {};
  3327. payload.fieldvalues = payloadFields;
  3328.  
  3329. // looping over Sara variable set to build the payload
  3330. rec = new GlideRecord('u_sara_variable');
  3331. //rec.addQuery('name', i);
  3332. rec.addQuery('variable_set.sys_id', varSetId);
  3333. rec.query();
  3334. while (rec.next()) {
  3335. var varName = rec.name;
  3336. var v = current.variables[varName];
  3337. payloadFields[rec.u_name] = [v.getDisplayValue()];
  3338. workflow.debug(varName + " = " + [v.getDisplayValue()]);
  3339. }
  3340.  
  3341.  
  3342. var json = new JSON();
  3343. var payloadStr = json.encode(payload);
  3344. workflow.debug(payloadStr);
  3345.  
  3346. workflow.scratchpad.executionHref = null;
  3347.  
  3348. // make request
  3349. if (serviceName) {
  3350. workflow.debug('Invoking consume URL...');
  3351.  
  3352. var request = restClient.newServiceRequest('Consume Request', 'post', consumeUrl);
  3353. request.setContent('${payload}');
  3354. request.addHeader('content-type', 'application/json;charset=UTF-8');
  3355. request.setStringParameterWithoutEscaping('payload', payloadStr);
  3356.  
  3357. // add Authorization header
  3358. request.addHeader('Authorization', tokens[3] + ' ' + tokens[0]);
  3359.  
  3360. var response = restClient.execute(request);
  3361.  
  3362. //
  3363. if (response && response.getStatusCode() == 401) {
  3364. var resObj = new JSONParser().parse(response.getBody());
  3365. var error = resObj['error'];
  3366. if (error == 'expired_token') {
  3367. workflow.debug('User token is expired, issuing request to refresh token ...');
  3368.  
  3369. // issue a refresh request
  3370. var tokens = oauthClient.refreshToken(workflow.scratchpad.openedBy);
  3371. if (tokens && !tokens.error) {
  3372. workflow.debug('Token refresh finished successfully');
  3373. workflow.debug('Got new tokens: ' + new JSON().encode(tokens));
  3374. oauthClient.storeUserTokens(workflow.scratchpad.openedBy, tokens['access_token'], tokens['refresh_token'], tokens['expires_in'], tokens['token_type']);
  3375.  
  3376. workflow.scratchpad.tokens = { 'accessToken': tokens['access_token'], 'refreshToken': tokens['refresh_token'], 'tokenType': tokens['token_type'] };
  3377.  
  3378. workflow.debug('Making new consume request...');
  3379. // re-initiate consume request with new token
  3380. request = restClient.newServiceRequest('Consume Request', 'post', consumeUrl);
  3381. request.setContent('${payload}');
  3382. request.addHeader('content-type', 'application/json');
  3383. request.setStringParameterWithoutEscaping('payload', payloadStr);
  3384. request.addHeader('Authorization', tokens['token_type'] + ' ' + tokens['access_token']);
  3385.  
  3386. response = restClient.execute(request);
  3387.  
  3388. } else {
  3389. workflow.debug('Refresh token finished unsuccessfully.');
  3390. if (tokens != null) {
  3391. workflow.debug('Error: ' + tokens.error);
  3392. oauthClient.removeUserToken(workflow.scratchpad.openedBy);
  3393. }
  3394. workflow.cancel(current);
  3395. }
  3396. } else if (error = 'invalid_token') {
  3397. workflow.debug('You\'ve got invalid tokens, please try again or contact administrator to solve this problem');
  3398. // invalidate token
  3399. oauthClient.removeUserToken(workflow.scratchpad.openedBy);
  3400. workflow.cancel(current);
  3401. }
  3402. }
  3403.  
  3404. if (!response || response.getStatusCode() < 200 || response.getStatusCode() > 299) {
  3405. workflow.debug('Request failed.');
  3406. workflow.debug(new JSON().encode(response));
  3407. if (response && response.getStatusCode()) {
  3408. workflow.debug("Status code: " + response.getStatusCode() + ", error message: " + response.getBody());
  3409. } else if (response) {
  3410. workflow.debug('No response from server, error message: ' + response.errorMessage);
  3411. } else workflow.debug('Response Null');
  3412. workflow.debug("Order process FAILED.");
  3413. } else {
  3414. workflow.debug('Successfully transfered.');
  3415. workflow.debug('Response: ' + response.getBody());
  3416. var resObj = json.decode(response.getBody());
  3417. var runId = resObj['runID'];
  3418. var executionHref = resObj['_links']['self']['href'];
  3419. workflow.debug('Got runId: ' + runId);
  3420. workflow.debug('Got execution href: ' + executionHref);
  3421. workflow.scratchpad.runId = runId;
  3422. workflow.scratchpad.executionHref = executionHref;
  3423. }
  3424.  
  3425. } else {
  3426. workflow.debug("Failed to make consume request as no service definition was defined.");
  3427. }
  3428. ]]></value>
  3429. </variable>
  3430. </variables>
  3431. </activity>
  3432. <activity>
  3433. <name>Check consume result</name>
  3434. <activity_definition>If</activity_definition>
  3435. <stage>Transfered</stage>
  3436. <width/>
  3437. <height/>
  3438. <x>310</x>
  3439. <y>450</y>
  3440. <snsc_name>check_consume</snsc_name>
  3441. <variables>
  3442. <variable>
  3443. <name>script</name>
  3444. <value><![CDATA[// This script needs to set answer to 'yes' or 'no' to indicate the state of the activity.
  3445. //
  3446. // For example,
  3447. //
  3448. answer = ifScript();
  3449.  
  3450. function ifScript() {
  3451. return (workflow.scratchpad.executionHref != null ? 'yes' : 'no');
  3452. }
  3453. ]]></value>
  3454. </variable>
  3455. <variable>
  3456. <name>advanced</name>
  3457. <value>1</value>
  3458. </variable>
  3459. </variables>
  3460. </activity>
  3461. <activity>
  3462. <name>Wait for 5 seconds</name>
  3463. <activity_definition>Timer</activity_definition>
  3464. <stage>Active</stage>
  3465. <width/>
  3466. <height/>
  3467. <x>490</x>
  3468. <y>280</y>
  3469. <snsc_name>wait_for_5</snsc_name>
  3470. <variables>
  3471. <variable>
  3472. <name>timer_type</name>
  3473. <value>script</value>
  3474. </variable>
  3475. <variable>
  3476. <name>script</name>
  3477. <value><![CDATA[// Set 'answer' to the number of seconds this timer should wait
  3478. answer = 5;
  3479. ]]></value>
  3480. </variable>
  3481. </variables>
  3482. </activity>
  3483. <activity>
  3484. <name>Retrieve Execution Status</name>
  3485. <activity_definition>Run Script</activity_definition>
  3486. <stage>Active</stage>
  3487. <width/>
  3488. <height/>
  3489. <x>730</x>
  3490. <y>320</y>
  3491. <snsc_name>retrieve_status</snsc_name>
  3492. <variables>
  3493. <variable>
  3494. <name>script</name>
  3495. <value><![CDATA[// input: response object from consume request
  3496. var restClient = new SaraRestClient();
  3497. restClient.setMIDServer(SaraCommon.getMidServer());
  3498. var executionHref = SaraUtils.buildUrl(workflow.scratchpad.executionHref);
  3499. var request = restClient.newServiceRequest('Sara Check Execution Status', 'get', executionHref);
  3500. request.addHeader('Authorization', workflow.scratchpad.tokens['tokenType'] + ' ' + workflow.scratchpad.tokens['accessToken']);
  3501.  
  3502. workflow.scratchpad.ended = 'unknown';
  3503.  
  3504. var counter = 1;
  3505. while (counter <= 300) {
  3506.  
  3507. workflow.debug(counter + ': Waiting for execution checking response ...');
  3508. //var response = restClient.execute(request);
  3509. var response = restClient.executeWithRefreshToken(request);
  3510.  
  3511. if (!response || response.getStatusCode() < 200 || response.getStatusCode() > 299) {
  3512. if (response && response.getStatusCode()) {
  3513. workflow.debug("Status code: " + response.getStatusCode() + ", error message: " + response.getBody());
  3514. } else if (response) workflow.debug('No response from server, error message: ' + response.errorMessage);
  3515. else workflow.debug('Response Null');
  3516. workflow.debug('Stop retrieving execution status now.');
  3517. break;
  3518. } else {
  3519. workflow.debug('Response received: ' + response.getBody());
  3520. var parser = new JSONParser();
  3521. var resObj = parser.parse(response.getBody());
  3522. var status = resObj.statusCode;
  3523. workflow.debug('Status code: ' + status + ', statusText: ' + resObj.statusText);
  3524. // Status code >= 1800 -> Task ended with ended code, otherwise, it is running
  3525. if (status >= 1800) {
  3526. if (status == '1900' || status == 1900) {
  3527. workflow.debug('Execution finished successfully!');
  3528. workflow.scratchpad.ended = 'ENDED_OK';
  3529. } else {
  3530. workflow.scratchpad.ended = status +' - '+resObj.statusText;
  3531. }
  3532. break;
  3533. } else {
  3534. // Status code < 1800 -> Task is active or running...
  3535. //workflow.debug('');
  3536. }
  3537. }
  3538.  
  3539. counter++;
  3540. gs.sleep(2000);
  3541.  
  3542. }
  3543.  
  3544. ]]></value>
  3545. </variable>
  3546. </variables>
  3547. </activity>
  3548. <activity>
  3549. <name>Check Execution Result</name>
  3550. <activity_definition>If</activity_definition>
  3551. <stage>Active</stage>
  3552. <width/>
  3553. <height/>
  3554. <x>970</x>
  3555. <y>310</y>
  3556. <snsc_name>check_result</snsc_name>
  3557. <variables>
  3558. <variable>
  3559. <name>script</name>
  3560. <value><![CDATA[// This script needs to set answer to 'yes' or 'no' to indicate the state of the activity.
  3561.  
  3562. answer = ifScript();
  3563.  
  3564. function ifScript() {
  3565. return (workflow.scratchpad.ended == 'ENDED_OK'?'yes':'no');
  3566. }
  3567.  
  3568. ]]></value>
  3569. </variable>
  3570. <variable>
  3571. <name>advanced</name>
  3572. <value>1</value>
  3573. </variable>
  3574. </variables>
  3575. </activity>
  3576. <activity>
  3577. <name>Consume Request Failed</name>
  3578. <activity_definition>Log Message</activity_definition>
  3579. <stage>Transfer Failed</stage>
  3580. <width/>
  3581. <height/>
  3582. <x>985</x>
  3583. <y>505</y>
  3584. <snsc_name>consume_failed</snsc_name>
  3585. <variables>
  3586. <variable>
  3587. <name>message</name>
  3588. <value>Consume request failed, order skipped.</value>
  3589. </variable>
  3590. </variables>
  3591. </activity>
  3592. <activity>
  3593. <name>Successfully Ended</name>
  3594. <activity_definition>Log Message</activity_definition>
  3595. <stage>Ended OK</stage>
  3596. <width/>
  3597. <height/>
  3598. <x>1200</x>
  3599. <y>280</y>
  3600. <snsc_name>ended_ok</snsc_name>
  3601. <variables>
  3602. <variable>
  3603. <name>message</name>
  3604. <value>Workflow finished successfully.</value>
  3605. </variable>
  3606. </variables>
  3607. </activity>
  3608. <activity>
  3609. <name>Ended NOT OK</name>
  3610. <activity_definition>Log Message</activity_definition>
  3611. <stage>Ended Failed</stage>
  3612. <width/>
  3613. <height/>
  3614. <x>1280</x>
  3615. <y>410</y>
  3616. <snsc_name>ended_not_ok</snsc_name>
  3617. <variables>
  3618. <variable>
  3619. <name>message</name>
  3620. <value>Workflow finished NOT OK.</value>
  3621. </variable>
  3622. </variables>
  3623. </activity>
  3624. <activity>
  3625. <name>End</name>
  3626. <activity_definition>End</activity_definition>
  3627. <stage/>
  3628. <width/>
  3629. <height/>
  3630. <x>1500</x>
  3631. <y>520</y>
  3632. <snsc_name>end</snsc_name>
  3633. </activity>
  3634. </activities>
  3635. <transitions>
  3636. <transition>
  3637. <from>begin</from>
  3638. <to>initialize</to>
  3639. <condition>
  3640. <name>Always</name>
  3641. <condition>true</condition>
  3642. <order>1</order>
  3643. <description/>
  3644. </condition>
  3645. </transition>
  3646. <transition>
  3647. <from>initialize</from>
  3648. <to>invoke_consume</to>
  3649. <condition>
  3650. <name>Always</name>
  3651. <condition>true</condition>
  3652. <order>1</order>
  3653. <description/>
  3654. </condition>
  3655. </transition>
  3656. <transition>
  3657. <from>invoke_consume</from>
  3658. <to>check_consume</to>
  3659. <condition>
  3660. <name>Always</name>
  3661. <condition>true</condition>
  3662. <order>1</order>
  3663. <description/>
  3664. </condition>
  3665. </transition>
  3666. <transition>
  3667. <from>check_consume</from>
  3668. <to>wait_for_5</to>
  3669. <condition>
  3670. <name>Yes</name>
  3671. <condition>activity.result == 'yes'</condition>
  3672. <order>1</order>
  3673. <description/>
  3674. </condition>
  3675. </transition>
  3676. <transition>
  3677. <from>check_consume</from>
  3678. <to>consume_failed</to>
  3679. <condition>
  3680. <name>No</name>
  3681. <condition>activity.result == 'no'</condition>
  3682. <order>1</order>
  3683. <description/>
  3684. </condition>
  3685. </transition>
  3686. <transition>
  3687. <from>consume_failed</from>
  3688. <to>end</to>
  3689. <condition>
  3690. <name>Always</name>
  3691. <condition>true</condition>
  3692. <order>1</order>
  3693. <description/>
  3694. </condition>
  3695. </transition>
  3696. <transition>
  3697. <from>wait_for_5</from>
  3698. <to>retrieve_status</to>
  3699. <condition>
  3700. <name>Always</name>
  3701. <condition>true</condition>
  3702. <order>1</order>
  3703. <description/>
  3704. </condition>
  3705. </transition>
  3706. <transition>
  3707. <from>retrieve_status</from>
  3708. <to>check_result</to>
  3709. <condition>
  3710. <name>Always</name>
  3711. <condition>true</condition>
  3712. <order>1</order>
  3713. <description/>
  3714. </condition>
  3715. </transition>
  3716. <transition>
  3717. <from>check_result</from>
  3718. <to>ended_ok</to>
  3719. <condition>
  3720. <name>Yes</name>
  3721. <condition>activity.result == 'yes'</condition>
  3722. <order>1</order>
  3723. <description/>
  3724. </condition>
  3725. </transition>
  3726. <transition>
  3727. <from>check_result</from>
  3728. <to>ended_not_ok</to>
  3729. <condition>
  3730. <name>No</name>
  3731. <condition>activity.result == 'no'</condition>
  3732. <order>2</order>
  3733. <description/>
  3734. </condition>
  3735. </transition>
  3736. <transition>
  3737. <from>ended_ok</from>
  3738. <to>end</to>
  3739. <condition>
  3740. <name>Always</name>
  3741. <condition>true</condition>
  3742. <order>1</order>
  3743. <description/>
  3744. </condition>
  3745. </transition>
  3746. <transition>
  3747. <from>ended_not_ok</from>
  3748. <to>end</to>
  3749. <condition>
  3750. <name>Always</name>
  3751. <condition>true</condition>
  3752. <order>1</order>
  3753. <description/>
  3754. </condition>
  3755. </transition>
  3756. </transitions>
  3757. </workflow>
  3758. </workflows>
  3759.  
  3760.  
  3761. <wizards>
  3762. <wizard>
  3763. <name>Automic OAuth Configuration</name>
  3764. <type>expert</type>
  3765. <roles>admin</roles>
  3766. <first_panel>OAuth Client</first_panel>
  3767. <variables>
  3768. <variable>
  3769. <name>client_id</name>
  3770. <type>6</type>
  3771. <class>expert_variable</class>
  3772. <question>Client ID</question>
  3773. <order>100</order>
  3774. <mandatory>false</mandatory>
  3775. <active>true</active>
  3776. <panel>OAuth Client</panel>
  3777. </variable>
  3778. <variable>
  3779. <name>client_secret</name>
  3780. <type>6</type>
  3781. <class>expert_variable</class>
  3782. <question>Client Secret</question>
  3783. <order>200</order>
  3784. <mandatory>false</mandatory>
  3785. <active>true</active>
  3786. <panel>OAuth Client</panel>
  3787. </variable>
  3788. <variable>
  3789. <name>sapi_endpoint</name>
  3790. <type>6</type>
  3791. <class>expert_variable</class>
  3792. <question>SAPI Endpoint</question>
  3793. <order>300</order>
  3794. <mandatory>false</mandatory>
  3795. <active>true</active>
  3796. <panel>OAuth Client</panel>
  3797. </variable>
  3798. <variable>
  3799. <name>token_endpoint</name>
  3800. <type>6</type>
  3801. <class>expert_variable</class>
  3802. <question>Token Endpoint</question>
  3803. <order>400</order>
  3804. <mandatory>false</mandatory>
  3805. <active>true</active>
  3806. <panel>OAuth Client</panel>
  3807. </variable>
  3808. <variable>
  3809. <name>auth_endpoint</name>
  3810. <type>6</type>
  3811. <class>expert_variable</class>
  3812. <question>Authorization Endpoint</question>
  3813. <order>500</order>
  3814. <mandatory>false</mandatory>
  3815. <active>true</active>
  3816. <panel>OAuth Client</panel>
  3817. </variable>
  3818. </variables>
  3819. <panels>
  3820. <panel>
  3821. <name>OAuth Client</name>
  3822. <title>OAuth Client</title>
  3823. <class>expert_panel</class>
  3824. <description>Prepare Service Now OAuth Client for SARA</description>
  3825. <previous_message>Previous</previous_message>
  3826. <next_message>Store OAuth Client Settings</next_message>
  3827. <complete_message>Done</complete_message>
  3828. <client_script>
  3829. <name>SARA OAuth Client OnLoad</name>
  3830. <active>true</active>
  3831. <type>onLoad</type>
  3832. <script><![CDATA[function onLoad() {
  3833. // load existing properties if available to form
  3834. var props = ['client_id', 'client_secret', 'sapi_endpoint', 'token_endpoint', 'auth_endpoint'];
  3835. for (i = 0; i < props.length; i++) {
  3836. var name = props[i];
  3837. g_form.setMandatory(name, true);
  3838. if (name == "client_secret"){
  3839. g_form.getControl('client_secret').type="password";
  3840. }
  3841. var rec = new GlideRecord('sys_properties');
  3842. var propName = 'com.automic.sara.' + name;
  3843. var ga = new GlideAjax("SaraCommonAjax");
  3844. ga.addParam("sysparm_name","getSysProperty");
  3845. ga.addParam("sysparm_property_name",propName);
  3846. ga.getXMLWait();
  3847. g_form.setValue(name , ga.getAnswer());
  3848. }
  3849. }
  3850. ]]></script>
  3851. </client_script>
  3852. </panel>
  3853. <panel>
  3854. <name>Completed</name>
  3855. <title>Complete</title>
  3856. <class>expert_panel</class>
  3857. <description>Complete</description>
  3858. <previous_message>Previous</previous_message>
  3859. <next_message>Next</next_message>
  3860. <complete_message>Done</complete_message>
  3861. </panel>
  3862. </panels>
  3863. <transitions>
  3864. <transition>
  3865. <from>OAuth Client</from>
  3866. <to>Completed</to>
  3867. <order>100</order>
  3868. <condition/>
  3869. <transition_script><![CDATA[gs.addInfoMessage('Storing OAuth client information into System Properties table ..');
  3870. gs.addInfoMessage('Client ID = ' + wizard.client_id + ', Client Secret = ***, SAPI Endpoint = ' + wizard.sapi_endpoint
  3871. + ', Token Endpoint = ' + wizard.token_endpoint + ', Authorization Endpoint = ' + wizard.auth_endpoint);
  3872.  
  3873. var oauthClient = new SaraOAuthClient();
  3874. try {
  3875. oauthClient.storeClientSettings(wizard.client_id, wizard.client_secret, wizard.sapi_endpoint, wizard.token_endpoint, wizard.auth_endpoint);
  3876. gs.addInfoMessage('Successfully update Automic OAuth client settings');
  3877. } catch (err) {
  3878. gs.addInfoMessage('Failed to update Automic OAuth client settings');
  3879. gs.log('Error on update Automic OAuth client settings: '+err,'SARA');
  3880. }
  3881.  
  3882. ]]></transition_script>
  3883. </transition>
  3884. </transitions>
  3885. </wizard>
  3886. <wizard>
  3887. <name>Automic Import Services</name>
  3888. <type>expert</type>
  3889. <roles>admin</roles>
  3890. <first_panel>Import Services</first_panel>
  3891. <variables>
  3892. <variable>
  3893. <name>sara_oauth_sysid</name>
  3894. <type>6</type>
  3895. <class>expert_variable</class>
  3896. <question>Sara OAuth SysId</question>
  3897. <order>400</order>
  3898. <mandatory>false</mandatory>
  3899. <active>true</active>
  3900. <panel>Import Services</panel>
  3901. </variable>
  3902. <variable>
  3903. <name>sara_username</name>
  3904. <type>6</type>
  3905. <class>expert_variable</class>
  3906. <question>SARA Username</question>
  3907. <order>100</order>
  3908. <mandatory>false</mandatory>
  3909. <active>true</active>
  3910. <panel>Import Services</panel>
  3911. </variable>
  3912. <variable>
  3913. <name>sara_password</name>
  3914. <type>6</type>
  3915. <class>expert_variable</class>
  3916. <question>SARA Password</question>
  3917. <order>200</order>
  3918. <mandatory>false</mandatory>
  3919. <active>true</active>
  3920. <panel>Import Services</panel>
  3921. </variable>
  3922. <variable>
  3923. <name>sara_target_category</name>
  3924. <type>18</type>
  3925. <class>expert_variable</class>
  3926. <question>Target Category</question>
  3927. <order>300</order>
  3928. <mandatory>false</mandatory>
  3929. <active>true</active>
  3930. <lookup_table>sc_category</lookup_table>
  3931. <lookup_value>sys_id</lookup_value>
  3932. <lookup_label>title</lookup_label>
  3933. <panel>Import Services</panel>
  3934. </variable>
  3935. </variables>
  3936. <panels>
  3937. <panel>
  3938. <name>Import Services</name>
  3939. <title>Import Services</title>
  3940. <class>expert_panel</class>
  3941. <description>Import SARA services to Service Catalog. Username/password are mandatory in cyou don't have tokens or your tokens invalid or expired.</description>
  3942. <previous_message>Previous</previous_message>
  3943. <next_message>Import Services</next_message>
  3944. <complete_message>Done</complete_message>
  3945. <client_script>
  3946. <name>SARA Import Service OnLoad</name>
  3947. <active>true</active>
  3948. <type>onLoad</type>
  3949. <script><![CDATA[function onLoad() {
  3950.  
  3951. // check if system properties are existed;
  3952. var props = ['client_id', 'client_secret', 'sapi_endpoint', 'token_endpoint', 'auth_endpoint'];
  3953. for (i = 0; i < props.length; i++) {
  3954. var name = props[i];
  3955. var rec = new GlideRecord('sys_properties');
  3956. var propName = 'com.automic.sara.' + name;
  3957. var ga = new GlideAjax("SaraCommonAjax");
  3958. ga.addParam("sysparm_name","getSysProperty");
  3959. ga.addParam("sysparm_property_name",propName);
  3960. ga.getXMLWait();
  3961. if (!ga.getAnswer()){
  3962. g_form.addErrorMessage("OAuth Configuration '"+props[i]+"' is empty. Please check the current OAuth configuration.");
  3963. document.getElementById('expert_next').style.display = 'none';
  3964. break;
  3965. }
  3966. }
  3967.  
  3968. g_form.setVisible('sara_oauth_sysid', false);
  3969. g_form.getControl('sara_password').type="password";
  3970. // check if Sara tokens exist for current user
  3971. var currentUser = g_user.userName;
  3972. var rec = new GlideRecord('u_sara_oauth');
  3973. rec.addQuery('u_user.user_name', currentUser);
  3974. rec.query(function(rec) {
  3975. if (rec.next()) {
  3976. g_form.setValue('sara_oauth_sysid', rec.u_access_token);
  3977. // hide username and password textbox
  3978. //g_form.setDisplay('sara_oauth_sysid',false);
  3979. g_form.setDisplay('sara_username', false);
  3980. g_form.setDisplay('sara_password', false);
  3981.  
  3982. } else {
  3983. g_form.setMandatory('sara_username', true);
  3984. g_form.showFieldMsg('sara_username','Username must be formatted as: <SYSTEM_NAME>/<CLIENT>/<USER>/<DEPARTMENT>. E.g. AE10/1/BOND/BOND');
  3985. g_form.setMandatory('sara_password', true);
  3986. }
  3987. });
  3988. }
  3989. ]]></script>
  3990. </client_script>
  3991. </panel>
  3992. <panel>
  3993. <name>Completed</name>
  3994. <title>Complete</title>
  3995. <class>expert_panel</class>
  3996. <description>Complete</description>
  3997. <previous_message>Previous</previous_message>
  3998. <next_message>Next</next_message>
  3999. <complete_message>Done</complete_message>
  4000. </panel>
  4001. </panels>
  4002. <transitions>
  4003. <transition>
  4004. <from>Import Services</from>
  4005. <to>Completed</to>
  4006. <order>100</order>
  4007. <condition>[sara_oauth_sysid]ISEMPTY^[sara_username]ISNOTEMPTY^EQ</condition>
  4008. <transition_script><![CDATA[gs.addInfoMessage('Requesting tokens for user \'' + gs.getUserName() + '\' with provided credentials ... ');
  4009.  
  4010. var oauthClient = new SaraOAuthClient();
  4011. // request new tokens for this user, store in 'u_sara_oauth' table
  4012. try {
  4013. tokens = oauthClient.requestToken(wizard.sara_username, wizard.sara_password);
  4014. } catch (err) {
  4015. gs.addErrorMessage('Failed to request user tokens');
  4016. gs.addErrorMessage('Error: ' + err);
  4017. }
  4018.  
  4019. if (!tokens || tokens['error'] || !tokens['access_token']) {
  4020. gs.addErrorMessage('Failed to request user tokens');
  4021. if (tokens['error']) gs.addErrorMessage('Error: ' + tokens['error'] + ', error description: ' + tokens['error_description']);
  4022. gs.addInfoMessage('Import finished unsuccessfully');
  4023. } else {
  4024. gs.addInfoMessage('Obtained tokens for \'' + gs.getUserName() + '\'');
  4025. oauthClient.storeUserTokens(gs.getUserName(), tokens['access_token'], tokens['refresh_token'], tokens['expires_in'], tokens['token_type']);
  4026. // process import
  4027. var tokens = oauthClient.getUserTokens(gs.getUserName());
  4028. if (tokens.length > 0 && tokens[0]) {
  4029. gs.addInfoMessage('Access Token = ' + tokens[0] + ', Secret Token = ' + tokens[1] + ', Token Expiry = ' + tokens[2] + ', Token Type = ' + tokens[3]);
  4030. gs.addInfoMessage('Importing services with provided tokens ...');
  4031. //var demo = new SaraDemo();
  4032. var consumer = new SAPIConsumer();
  4033. try {
  4034. var result = consumer.discoverAndImport();
  4035. if (!result) gs.addInfoMessage('Got no service or catalog from server');
  4036. else gs.addInfoMessage('Imported successfully : ' + result);
  4037. } catch (err) {
  4038. gs.addErrorMessage('Failed to import SARA services');
  4039. gs.addErrorMessage(err);
  4040. }
  4041. } else {
  4042. gs.addErrorMessage('No token found, skip import');
  4043. }
  4044. }
  4045. ]]></transition_script>
  4046. </transition>
  4047. <transition>
  4048. <from>Import Services</from>
  4049. <to>Completed</to>
  4050. <order>100</order>
  4051. <condition>[sara_oauth_sysid]ISNOTEMPTY^EQ</condition>
  4052. <transition_script><![CDATA[// tokens exist, just process import
  4053.  
  4054. var tokens = new SaraOAuthClient().getUserTokens(gs.getUserName());
  4055. if (tokens.length > 0) {
  4056. gs.addInfoMessage('Access Token = ' + tokens[0] + ', Secret Token = ' + tokens[1] + ', Token Expiry = ' + tokens[2]);
  4057. gs.addInfoMessage('Importing services with provided tokens ...');
  4058. //var demo = new SaraDemo();
  4059. var consumer = new SAPIConsumer();
  4060. try {
  4061. //var result = demo.discoverAndImport();
  4062. var result = consumer.discoverAndImport();
  4063. if (!result){
  4064. var statusCode = consumer.getStatusCode();
  4065. var errorMessage = consumer.getErrorMessage();
  4066. if (statusCode != 200){
  4067. gs.addErrorMessage('Failed to import Automic services');
  4068. gs.addErrorMessage('Got error: ' + statusCode +' - '+errorMessage );
  4069. } else gs.addInfoMessage('Got no service or catalog from server.');
  4070. } else gs.addInfoMessage('Imported successfully : ' + result);
  4071. } catch (err) {
  4072. gs.addErrorMessage('Failed to import Automic services');
  4073. gs.addErrorMessage('Error on processing data from server');
  4074. gs.log('Got error from import service wizard panel for case oauth_sysid is not empty: ' + err,'SARA');
  4075. }
  4076. } else {
  4077. gs.addErrorMessage('No token found, skip import');
  4078. }
  4079. ]]></transition_script>
  4080. </transition>
  4081. <transition>
  4082. <from>Import Services</from>
  4083. <to>Completed</to>
  4084. <order>100</order>
  4085. <condition>[sara_oauth_sysid]ISEMPTY^[sara_username]ISEMPTY^EQ</condition>
  4086. <transition_script><![CDATA[
  4087. // with mandatory username/password enforced, this case actually couldn't happen
  4088. gs.addInfoMessage('No tokens found for current user. You have to provide credentials to get tokens!');
  4089. ]]></transition_script>
  4090. </transition>
  4091. </transitions>
  4092. </wizard>
  4093. <wizard>
  4094. <name>Automic Clean-up</name>
  4095. <type>expert</type>
  4096. <roles>admin</roles>
  4097. <first_panel>Automic Cleanup</first_panel>
  4098. <variables>
  4099. <variable/>
  4100. </variables>
  4101. <panels>
  4102. <panel>
  4103. <name>Automic Cleanup</name>
  4104. <title>Cleanup Automic catalog items</title>
  4105. <class>expert_panel</class>
  4106. <description>Cleanup all Automic SARA stuffs. This will delete all subcategories and service items.</description>
  4107. <previous_message>Previous</previous_message>
  4108. <next_message>Cleanup</next_message>
  4109. <complete_message>Done</complete_message>
  4110. </panel>
  4111. <panel>
  4112. <name>Completed</name>
  4113. <title>Cleanup Completed</title>
  4114. <class>expert_panel</class>
  4115. <description>Cleanup successfully!</description>
  4116. <previous_message>Previous</previous_message>
  4117. <next_message>Next</next_message>
  4118. <complete_message>Done</complete_message>
  4119. </panel>
  4120. </panels>
  4121. <transitions>
  4122. <transition>
  4123. <from>Automic Cleanup</from>
  4124. <to>Completed</to>
  4125. <order>100</order>
  4126. <condition/>
  4127. <transition_script><![CDATA[SaraUtils.cleanup();]]></transition_script>
  4128. </transition>
  4129. </transitions>
  4130.  
  4131. </wizard>
  4132. </wizards>
  4133.  
  4134. </package>
  4135.  
  4136. </sara>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement