説明なし

common.js 58KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917
  1. $.fn.serializeObject = function() {
  2. var o = {};
  3. var a = this.serializeArray();
  4. $.each(a, function() {
  5. if (o[this.name]) {
  6. if (!o[this.name].push) {
  7. o[this.name] = [o[this.name]];
  8. }
  9. o[this.name].push(this.value || '');
  10. } else {
  11. o[this.name] = this.value || '';
  12. }
  13. });
  14. return o;
  15. };
  16. var jdata_menu_options = [];
  17. let current_cid = null;
  18. function clear_api_error() {
  19. $(".invalid-feedback").hide();
  20. }
  21. function setCookie(name,value,days) {
  22. var expires = "";
  23. if (days) {
  24. var date = new Date();
  25. date.setTime(date.getTime() + (days*24*60*60*1000));
  26. expires = "; expires=" + date.toUTCString();
  27. }
  28. document.cookie = name + "=" + (value || "") + expires + "; path=/";
  29. }
  30. function getCookie(name) {
  31. var nameEQ = name + "=";
  32. var ca = document.cookie.split(';');
  33. for(var i=0;i < ca.length;i++) {
  34. var c = ca[i];
  35. while (c.charAt(0)==' ') c = c.substring(1,c.length);
  36. if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  37. }
  38. return null;
  39. }
  40. function eraseCookie(name) {
  41. document.cookie = name +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
  42. }
  43. function ellipsis_field( data, cutoff, wordbreak ) {
  44. data = data.toString();
  45. let anchor = $('<div>');
  46. if ( data.length <= cutoff ) {
  47. anchor.text(data);
  48. return anchor.prop('outerHTML');
  49. }
  50. let shortened = data.substr(0, cutoff-1);
  51. // Find the last white space character in the string
  52. if ( wordbreak ) {
  53. shortened = shortened.replace(/\s([^\s]*)$/, '');
  54. }
  55. // Build a new anchor tag with the new target
  56. anchor.text(shortened + '…');
  57. anchor.className = 'ellipsis';
  58. anchor.title = data;
  59. return anchor.prop('outerHTML');
  60. }
  61. function ret_obj_dt_description(data) {
  62. let anchor = $('<span>');
  63. let dataContent = typeof data === 'object' ? JSON.stringify(data) : data;
  64. anchor.attr('data-toggle', 'popover')
  65. .attr('data-trigger', 'hover')
  66. .attr('title', 'Description')
  67. .attr('data-content', dataContent)
  68. .attr('href', '#')
  69. .css('cursor', 'pointer')
  70. .text(ellipsis_field_raw(data, 64));
  71. return anchor.prop('outerHTML');
  72. }
  73. function render_date(date, show_ms = false) {
  74. // Remove the timezone information and the ms
  75. let date_str = date.replace('T', ' ').replace('Z', '');
  76. if (!show_ms) {
  77. date_str = date_str.split('.')[0];
  78. } else {
  79. // remove nanoseconds
  80. date_str = date_str.split('.')[0] + '.' + date_str.split('.')[1].substr(0, 3);
  81. }
  82. return date_str;
  83. }
  84. function ellipsis_field_raw( data, cutoff, wordbreak ) {
  85. if (data === undefined || data === null) {
  86. return '';
  87. }
  88. if (data.length <= cutoff) {
  89. return data;
  90. }
  91. let shortened = data.substr(0, cutoff - 1);
  92. if (wordbreak) {
  93. shortened = shortened.replace(/\s([^\s]*)$/, '');
  94. }
  95. return shortened + '…';
  96. }
  97. function propagate_form_api_errors(data_error) {
  98. // TODO instead of typeof (' '), why not just put 'string'?
  99. if (typeof (data_error) === typeof (' ')) {
  100. notify_error(data_error);
  101. return;
  102. }
  103. for (let e in data_error) {
  104. if($("#" + e).length !== 0) {
  105. $("#" + e).addClass('is-invalid');
  106. errors = ""
  107. for (n in data_error[e]) {
  108. errors += data_error[e][n];
  109. }
  110. if($("#" + e + "-invalid-msg").length !== 0) {
  111. $("#" + e + "-invalid-msg").remove();
  112. }
  113. $("#" + e).after("<div class='invalid-feedback' id='" + e + "-invalid-msg'>" + errors +"</div>");
  114. $("#" + e + "-invalid-msg").show();
  115. }
  116. else {
  117. msg = e + " - ";
  118. for (n in data_error[e]) {
  119. msg += data_error[e][n];
  120. }
  121. notify_error(msg);
  122. }
  123. }
  124. }
  125. function ajax_notify_error(jqXHR, url) {
  126. if (jqXHR.status == 403) {
  127. message = 'Permission denied';
  128. } else {
  129. message = `We got error ${jqXHR.status} - ${jqXHR.statusText} requesting ${url}`;
  130. }
  131. notify_error(message);
  132. }
  133. function notify_error(message) {
  134. let p = $('<p>')
  135. p.text(message);
  136. let data = "";
  137. if (typeof (message) === typeof ([])) {
  138. for (element in message) {
  139. data += element
  140. }
  141. } else {
  142. data = message;
  143. }
  144. p.text(data)
  145. $.notify({
  146. icon: 'fas fa-triangle-exclamation',
  147. message: p.prop('outerHTML'),
  148. title: 'Error'
  149. }, {
  150. type: 'danger',
  151. placement: {
  152. from: 'bottom',
  153. align: 'left'
  154. },
  155. z_index: 2000,
  156. timer: 8000,
  157. animate: {
  158. enter: 'animated fadeIn',
  159. exit: 'animated fadeOut'
  160. }
  161. });
  162. }
  163. function get_tag_from_data(data, classes) {
  164. if (data === undefined || data === null || data.length === 0) {
  165. return '';
  166. }
  167. let tag_anchor = $('<span>');
  168. tag_anchor.addClass(classes);
  169. tag_anchor.text(data);
  170. tag_anchor.html('<i class="fa-solid fa-tag mr-1"></i> ' + tag_anchor.html());
  171. return tag_anchor.prop('outerHTML');
  172. }
  173. function get_ioc_tag_from_data(data, classes) {
  174. let tag_anchor = $('<span>');
  175. tag_anchor.addClass(classes);
  176. tag_anchor.text(data);
  177. tag_anchor.html('<i class="fa-solid fa-virus"></i> ' + tag_anchor.html());
  178. return tag_anchor.prop('outerHTML');
  179. }
  180. function notify_success(message) {
  181. let p = $('<p>')
  182. p.text(message);
  183. $.notify({
  184. icon: 'fas fa-check',
  185. message: p.prop('outerHTML')
  186. }, {
  187. type: 'success',
  188. placement: {
  189. from: 'bottom',
  190. align: 'left'
  191. },
  192. z_index: 2000,
  193. timer: 2500,
  194. animate: {
  195. enter: 'animated fadeIn',
  196. exit: 'animated fadeOut'
  197. }
  198. });
  199. }
  200. function notify_api_request_success(data) {
  201. if (data.message.length === 0) {
  202. data.message = 'Operation succeeded';
  203. }
  204. notify_success(data.message);
  205. }
  206. function notify_api_request_error(data) {
  207. if (data.message.length === 0) {
  208. data.message = 'Operation failed';
  209. }
  210. notify_error(data.message);
  211. }
  212. function api_request_failed(data) {
  213. if (data.status === 'success') {
  214. return false;
  215. }
  216. notify_api_request_error(data);
  217. return true;
  218. }
  219. function notify_auto_api(data) {
  220. if (data.status === 'success') {
  221. notify_api_request_success(data);
  222. return true;
  223. }
  224. notify_api_request_error(data);
  225. return false;
  226. }
  227. function get_request_api(uri, propagate_api_error, beforeSend_fn, cid) {
  228. if (cid === undefined ) {
  229. cid = case_param();
  230. } else {
  231. cid = '?cid=' + cid;
  232. }
  233. uri = uri + cid;
  234. return get_raw_request_api(uri, propagate_api_error, beforeSend_fn)
  235. }
  236. function get_raw_request_api(uri, propagate_api_error, beforeSend_fn) {
  237. return $.ajax({
  238. url: uri,
  239. type: 'GET',
  240. dataType: "json",
  241. beforeSend: function(jqXHR, settings) {
  242. if (beforeSend_fn !== undefined && beforeSend_fn !== null) {
  243. beforeSend_fn(jqXHR, settings);
  244. }
  245. },
  246. error: function(jqXHR) {
  247. if (propagate_api_error) {
  248. if(jqXHR.responseJSON && jqXHR.status === 400) {
  249. propagate_form_api_errors(jqXHR.responseJSON.data);
  250. } else {
  251. ajax_notify_error(jqXHR, this.url);
  252. }
  253. } else {
  254. if (jqXHR.status !== 400) {
  255. if(jqXHR.responseJSON) {
  256. notify_error(jqXHR.responseJSON.message);
  257. } else {
  258. ajax_notify_error(jqXHR, this.url);
  259. }
  260. }
  261. }
  262. }
  263. });
  264. }
  265. function set_page_warning(msg) {
  266. $('#page_warning').text(msg);
  267. }
  268. function api_error(propagate_api_error) {
  269. if (propagate_api_error) {
  270. if(jqXHR.responseJSON && jqXHR.status == 400) {
  271. propagate_form_api_errors(jqXHR.responseJSON.data);
  272. } else {
  273. ajax_notify_error(jqXHR, this.url);
  274. }
  275. } else {
  276. if(jqXHR.responseJSON) {
  277. notify_error(jqXHR.responseJSON.message);
  278. } else {
  279. ajax_notify_error(jqXHR, this.url);
  280. }
  281. }
  282. }
  283. function sendBefore(beforeSend_fn, settings, jqXHR) {
  284. if (beforeSend_fn !== undefined) {
  285. beforeSend_fn(jqXHR, settings);
  286. }
  287. }
  288. function get_request_data_api(uri, data, propagate_api_error, beforeSend_fn) {
  289. return $.ajax({
  290. url: uri + case_param(),
  291. type: 'GET',
  292. data: data,
  293. dataType: "json",
  294. beforeSend: function(jqXHR, settings) {
  295. sendBefore(beforeSend_fn, settings, jqXHR);
  296. },
  297. error: function(jqXHR) {
  298. api_error(propagate_api_error);
  299. }
  300. });
  301. }
  302. function delete_request_api(uri, data, propagate_api_error, beforeSend_fn) {
  303. return $.ajax({
  304. url: uri + case_param(),
  305. type: 'DELETE',
  306. data: data,
  307. dataType: "json",
  308. beforeSend: function(jqXHR, settings) {
  309. sendBefore(beforeSend_fn, settings, jqXHR);
  310. },
  311. error: function(jqXHR) {
  312. api_error(propagate_api_error);
  313. }
  314. });
  315. }
  316. function post_request_api(uri, data, propagate_api_error, beforeSend_fn, cid, onError_fn) {
  317. if (cid === undefined ) {
  318. cid = case_param();
  319. } else {
  320. cid = '?cid=' + cid;
  321. }
  322. if (data === undefined || data === null) {
  323. data = JSON.stringify({
  324. 'csrf_token': $('#csrf_token').val()
  325. });
  326. }
  327. return $.ajax({
  328. url: uri + cid,
  329. type: 'POST',
  330. data: data,
  331. dataType: 'json',
  332. contentType: 'application/json;charset=UTF-8',
  333. beforeSend: function(jqXHR, settings) {
  334. if (typeof beforeSend_fn === 'function') {
  335. beforeSend_fn(jqXHR, settings);
  336. }
  337. },
  338. error: function(jqXHR) {
  339. if (propagate_api_error) {
  340. if (jqXHR.responseJSON && jqXHR.status == 400) {
  341. propagate_form_api_errors(jqXHR.responseJSON.data);
  342. } else {
  343. ajax_notify_error(jqXHR, this.url);
  344. }
  345. } else {
  346. if (jqXHR.responseJSON) {
  347. notify_error(jqXHR.responseJSON.message);
  348. } else {
  349. ajax_notify_error(jqXHR, this.url);
  350. }
  351. }
  352. }
  353. });
  354. }
  355. function put_request_api(uri, data) {
  356. return $.ajax({
  357. url: uri,
  358. type: 'PUT',
  359. data: data,
  360. dataType: 'json',
  361. contentType: 'application/json;charset=UTF-8',
  362. error: function(jqXHR) {
  363. if (jqXHR.responseJSON && jqXHR.status == 400) {
  364. propagate_form_api_errors(jqXHR.responseJSON.data);
  365. } else {
  366. ajax_notify_error(jqXHR, this.url);
  367. }
  368. }
  369. });
  370. }
  371. function post_request_data_api(uri, data, beforeSend_fn) {
  372. return $.ajax({
  373. url: uri + case_param(),
  374. type: 'POST',
  375. data: data,
  376. dataType: 'json',
  377. contentType: false,
  378. processData: false,
  379. beforeSend: function(jqXHR, settings) {
  380. sendBefore(beforeSend_fn, settings, jqXHR);
  381. },
  382. error: function(jqXHR) {
  383. api_error(true);
  384. }
  385. });
  386. }
  387. function updateURLParameter(url, param, paramVal) {
  388. var TheAnchor = null;
  389. var newAdditionalURL = "";
  390. var tempArray = url.split("?");
  391. var baseURL = tempArray[0];
  392. var additionalURL = tempArray[1];
  393. var temp = "";
  394. if (additionalURL)
  395. {
  396. var tmpAnchor = additionalURL.split("#");
  397. var TheParams = tmpAnchor[0];
  398. TheAnchor = tmpAnchor[1];
  399. if(TheAnchor)
  400. additionalURL = TheParams;
  401. tempArray = additionalURL.split("&");
  402. for (var i=0; i<tempArray.length; i++)
  403. {
  404. if(tempArray[i].split('=')[0] != param)
  405. {
  406. newAdditionalURL += temp + tempArray[i];
  407. temp = "&";
  408. }
  409. }
  410. }
  411. else
  412. {
  413. var tmpAnchor = baseURL.split("#");
  414. var TheParams = tmpAnchor[0];
  415. TheAnchor = tmpAnchor[1];
  416. if(TheParams)
  417. baseURL = TheParams;
  418. }
  419. if(TheAnchor)
  420. paramVal += "#" + TheAnchor;
  421. var rows_txt = temp + "" + param + "=" + paramVal;
  422. return baseURL + "?" + newAdditionalURL + rows_txt;
  423. }
  424. function get_caseid() {
  425. if (current_cid === null) {
  426. let queryString = window.location.search;
  427. let urlParams = new URLSearchParams(queryString);
  428. current_cid = urlParams.get('cid')
  429. }
  430. return current_cid
  431. }
  432. function is_redirect() {
  433. queryString = window.location.search;
  434. urlParams = new URLSearchParams(queryString);
  435. return urlParams.get('redirect')
  436. }
  437. function notify_redirect() {
  438. if (is_redirect()) {
  439. swal("You've been redirected",
  440. "The case you attempted to reach wasn't found.\nYou have been redirected to a default case.",
  441. "info", {button: "OK"}
  442. ).then((value) => {
  443. queryString = window.location.search;
  444. urlParams = new URLSearchParams(queryString);
  445. urlParams.delete('redirect');
  446. history.replaceState(null, null, window.location.pathname + '?' + urlParams.toString());
  447. });
  448. }
  449. }
  450. function case_param() {
  451. var params = {
  452. cid: get_caseid
  453. }
  454. return '?'+ $.param(params);
  455. }
  456. var last_state = null;
  457. var need_check = true;
  458. function update_last_resfresh() {
  459. need_check = true;
  460. $('#last_resfresh').text("").removeClass("text-warning");
  461. }
  462. function check_update(url) {
  463. if (!need_check) {
  464. return;
  465. }
  466. $.ajax({
  467. url: url + case_param(),
  468. type: "GET",
  469. dataType: "json",
  470. success: function(data) {
  471. if (last_state == null || last_state < data.data.object_state) {
  472. $('#last_resfresh').text("Updates available").addClass("text-warning");
  473. need_check = false;
  474. }
  475. },
  476. error: function (data) {
  477. if (data.status == 404) {
  478. swal("Stop everything !",
  479. "The case you are working on was deleted",
  480. "error",
  481. {
  482. buttons: {
  483. again: {
  484. text: "Go to my default case",
  485. value: "default"
  486. }
  487. }
  488. }
  489. ).then((value) => {
  490. switch (value) {
  491. case "dash":
  492. location.reload();
  493. break;
  494. default:
  495. location.reload();
  496. }
  497. });
  498. } else if (data.status == 403) {
  499. window.location.replace("/case" + case_param());
  500. } else if (data.status == 400) {
  501. } else {
  502. notify_error('Connection with server lost');
  503. }
  504. }
  505. });
  506. }
  507. function set_last_state(state) {
  508. if (state != null) {
  509. last_state = state.object_state;
  510. }
  511. update_last_resfresh();
  512. }
  513. function show_loader() {
  514. $('#loading_msg').show();
  515. $('#card_main_load').hide();
  516. }
  517. function hide_loader() {
  518. $('#loading_msg').hide();
  519. $('#card_main_load').show();
  520. update_last_resfresh();
  521. }
  522. function list_to_badges(wordlist, style, limit, type) {
  523. badges = "";
  524. if (wordlist.length > limit) {
  525. badges = `<span class="badge badge-${style} ml-2">${wordlist.length} ${type}</span>`;
  526. }
  527. else {
  528. wordlist.forEach(function (item, index) {
  529. badges += `<span class="badge badge-${style} ml-2">${sanitizeHTML(item)}</span>`;
  530. });
  531. }
  532. return badges;
  533. }
  534. var sanitizeHTML = function (str, options) {
  535. if (options) {
  536. return filterXSS(str, options);
  537. } else {
  538. // Escape the html by default
  539. return filterXSS(str);
  540. }
  541. };
  542. function isWhiteSpace(s) {
  543. return /^\s+$/.test(s);
  544. }
  545. function exportInnerPng() {
  546. close_sid_var = document.querySelector(".close-quick-sidebar");
  547. close_sid_var.click();
  548. div = document.querySelector(".page-inner");
  549. html2canvas(div, {
  550. useCORS: true,
  551. scale: 3,
  552. backgroundColor: "#f9fbfd"
  553. }).then(canvas => {
  554. downloadURI(canvas.toDataURL(), 'iris'+location.pathname.replace('/', '_') + '.png')
  555. });
  556. }
  557. function downloadURI(uri, name) {
  558. var link = document.createElement("a");
  559. link.download = name;
  560. link.href = uri;
  561. document.body.appendChild(link);
  562. link.click();
  563. link.remove();
  564. }
  565. function copy_object_link(node_id) {
  566. link = buildShareLink(node_id);
  567. navigator.clipboard.writeText(link).then(function() {
  568. notify_success('Shared link copied');
  569. }, function(err) {
  570. notify_error('Can\'t copy link. I printed it in console.');
  571. console.error('Shared link', err);
  572. });
  573. }
  574. function capitalizeFirstLetter(string) {
  575. return string.charAt(0).toUpperCase() + string.slice(1);
  576. }
  577. function copy_object_link_md(data_type, node_id){
  578. let link = `[<i class="fa-solid fa-tag"></i> ${capitalizeFirstLetter(data_type)} #${node_id}](${buildShareLink(node_id)})`
  579. navigator.clipboard.writeText(link).then(function() {
  580. notify_success('MD link copied');
  581. }, function(err) {
  582. notify_error('Can\'t copy link. I printed it in console.');
  583. console.error('Shared link', err);
  584. });
  585. }
  586. function copy_text_clipboardb(data){
  587. navigator.clipboard.writeText(fromBinary64(data)).then(function() {
  588. notify_success('Copied');
  589. }, function(err) {
  590. notify_error('Can\'t copy link. I printed it in console.');
  591. console.error(err);
  592. });
  593. }
  594. function copy_text_clipboard(data){
  595. navigator.clipboard.writeText(data).then(function() {
  596. notify_success('Copied');
  597. }, function(err) {
  598. notify_error('Can\'t copy link. I printed it in console.');
  599. console.error(err);
  600. });
  601. }
  602. function load_case_activity(){
  603. get_request_api('/case/activities/list')
  604. .done((data) => {
  605. js_data = data.data;
  606. $('#case_activities').empty();
  607. for (index in js_data) {
  608. if (js_data[index].is_from_api) {
  609. api_flag = 'feed-item-primary';
  610. title = 'Activity issued from API';
  611. } else {
  612. api_flag = 'feed-item-default';
  613. title = 'Activity issued from GUI';
  614. }
  615. entry = `<li class="feed-item ${api_flag}" title='${sanitizeHTML(title)}'>
  616. <time class="date" datetime="${js_data[index].activity_date}">${formatTime(js_data[index].activity_date)}</time>
  617. <span class="text">${sanitizeHTML(js_data[index].name)} - ${sanitizeHTML(js_data[index].activity_desc)}</span>
  618. </li>`
  619. $('#case_activities').append(entry);
  620. }
  621. });
  622. }
  623. function load_dim_limited_tasks(){
  624. get_request_api('/dim/tasks/list/100')
  625. .done((data) => {
  626. js_data = data.data;
  627. $('#dim_tasks_feed').empty();
  628. for (index in js_data) {
  629. if (js_data[index].state == 'success') {
  630. api_flag = 'feed-item-success';
  631. title = 'Task succeeded';
  632. } else {
  633. api_flag = 'feed-item-warning';
  634. title = 'Task pending or failed';
  635. }
  636. entry = `<li class="feed-item ${api_flag}" title='${title}'>
  637. <time class="date" datetime="${js_data[index].activity_date}">${js_data[index].date_done}</time>
  638. <span class="text" title="${js_data[index].task_id}"><a href="#" onclick='dim_task_status("${js_data[index].task_id}");return false;'>${js_data[index].module}</a> - ${js_data[index].user}</span>
  639. </li>`
  640. $('#dim_tasks_feed').append(entry);
  641. }
  642. });
  643. }
  644. function dim_task_status(id) {
  645. url = '/dim/tasks/status/'+id + case_param();
  646. $('#info_dim_task_modal_body').load(url, function (response, status, xhr) {
  647. if (status !== "success") {
  648. ajax_notify_error(xhr, url);
  649. return false;
  650. }
  651. $('#modal_dim_task_detail').modal({show:true});
  652. });
  653. }
  654. function init_module_processing_wrap(rows, data_type, out_hook_name) {
  655. console.log(out_hook_name);
  656. hook_name = null;
  657. for (opt in jdata_menu_options) {
  658. console.log(jdata_menu_options[opt]);
  659. if (jdata_menu_options[opt].manual_hook_ui_name == out_hook_name) {
  660. hook_name = jdata_menu_options[opt].hook_name;
  661. hook_ui_name = jdata_menu_options[opt].manual_hook_ui_name;
  662. module_name = jdata_menu_options[opt].module_name;
  663. break
  664. }
  665. }
  666. if (hook_name == null) {
  667. notify_error('Error: hook not found');
  668. return false;
  669. }
  670. return init_module_processing(rows, hook_name, hook_ui_name, module_name, data_type);
  671. }
  672. function init_module_processing(rows, hook_name, hook_ui_name, module_name, data_type) {
  673. var data = Object();
  674. data['hook_name'] = hook_name;
  675. data['module_name'] = module_name;
  676. data['hook_ui_name'] = hook_ui_name;
  677. data['csrf_token'] = $('#csrf_token').val();
  678. data['type'] = data_type;
  679. data['targets'] = [];
  680. type_map = {
  681. "ioc": "ioc_id",
  682. "asset": "asset_id",
  683. "task": "task_id",
  684. "global_task": "task_id",
  685. "evidence": "id"
  686. }
  687. for (index in rows) {
  688. if (typeof rows[index] === 'object') {
  689. data['targets'].push(rows[index][type_map[data_type]]);
  690. } else {
  691. data['targets'].push(rows[index]);
  692. }
  693. }
  694. post_request_api("/dim/hooks/call", JSON.stringify(data), true)
  695. .done(function (data){
  696. notify_auto_api(data)
  697. });
  698. }
  699. function load_menu_mod_options_modal(element_id, data_type, anchor) {
  700. get_request_api(`/dim/hooks/options/${data_type}/list`)
  701. .done(function (data) {
  702. if (api_request_failed(data)) {
  703. return;
  704. }
  705. if (data.data != null) {
  706. let jsdata = data.data;
  707. if (jsdata.length !== 0 && anchor.children().length !== 0){
  708. anchor.append('<div class="dropdown-divider"></div>');
  709. }
  710. for (option in jsdata) {
  711. let opt = jsdata[option];
  712. let menu_opt = `<a class="dropdown-item" href="#" onclick='init_module_processing(["${element_id}"], "${opt.hook_name}",`+
  713. `"${opt.manual_hook_ui_name}","${opt.module_name}","${data_type}");return false;'><i class="fa fa-arrow-alt-circle-right mr-2"></i> ${opt.manual_hook_ui_name}</a>`
  714. anchor.append(menu_opt);
  715. }
  716. }
  717. })
  718. }
  719. function get_row_id(row) {
  720. let ids_map = ["ioc_id","asset_id","task_id","id"];
  721. for (let id in ids_map) {
  722. if (row[ids_map[id]] !== undefined) {
  723. return row[ids_map[id]];
  724. }
  725. }
  726. return null;
  727. }
  728. function get_row_value(row, column) {
  729. let ids_map = ["asset_name","ioc_value","filename","id"];
  730. for (let id in ids_map) {
  731. if (row[ids_map[id]] !== undefined) {
  732. return row[ids_map[id]];
  733. }
  734. }
  735. return null;
  736. }
  737. var iClassWhiteList = ['fa-solid fa-tags','fa-solid fa-tag', 'fa-solid fa-bell', 'fa-solid fa-virus-covid text-danger mr-1',
  738. 'fa-solid fa-file-shield text-success mr-1', 'fa-regular fa-file mr-1', 'fa-solid fa-lock text-success mr-1']
  739. function get_new_ace_editor(anchor_id, content_anchor, target_anchor, onchange_callback, do_save, readonly, live_preview) {
  740. var editor = ace.edit(anchor_id);
  741. if ($("#"+anchor_id).attr("data-theme") != "dark") {
  742. editor.setTheme("ace/theme/tomorrow");
  743. } else {
  744. editor.setTheme("ace/theme/iris_night");
  745. }
  746. editor.session.setMode("ace/mode/markdown");
  747. if (readonly !== undefined) {
  748. editor.setReadOnly(readonly);
  749. }
  750. editor.renderer.setShowGutter(true);
  751. editor.setOption("showLineNumbers", true);
  752. editor.setOption("showPrintMargin", false);
  753. editor.setOption("displayIndentGuides", true);
  754. editor.setOption("maxLines", "Infinity");
  755. editor.setOption("minLines", "2");
  756. editor.setOption("autoScrollEditorIntoView", true);
  757. editor.session.setUseWrapMode(true);
  758. editor.setOption("indentedSoftWrap", false);
  759. editor.renderer.setScrollMargin(8, 5)
  760. editor.setOption("enableBasicAutocompletion", true);
  761. if (do_save !== undefined && do_save !== null) {
  762. editor.commands.addCommand({
  763. name: 'save',
  764. bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
  765. exec: function(editor) {
  766. do_save()
  767. }
  768. });
  769. }
  770. editor.commands.addCommand({
  771. name: 'bold',
  772. bindKey: {win: "Ctrl-B", "mac": "Cmd-B"},
  773. exec: function(editor) {
  774. editor.insertSnippet('**${1:$SELECTION}**');
  775. }
  776. });
  777. editor.commands.addCommand({
  778. name: 'italic',
  779. bindKey: {win: "Ctrl-I", "mac": "Cmd-I"},
  780. exec: function(editor) {
  781. editor.insertSnippet('*${1:$SELECTION}*');
  782. }
  783. });
  784. editor.commands.addCommand({
  785. name: 'head_1',
  786. bindKey: {win: "Ctrl-Shift-1", "mac": "Cmd-Shift-1"},
  787. exec: function(editor) {
  788. editor.insertSnippet('# ${1:$SELECTION}');
  789. }
  790. });
  791. editor.commands.addCommand({
  792. name: 'head_2',
  793. bindKey: {win: "Ctrl-Shift-2", "mac": "Cmd-Shift-2"},
  794. exec: function(editor) {
  795. editor.insertSnippet('## ${1:$SELECTION}');
  796. }
  797. });
  798. editor.commands.addCommand({
  799. name: 'head_3',
  800. bindKey: {win: "Ctrl-Shift-3", "mac": "Cmd-Shift-3"},
  801. exec: function(editor) {
  802. editor.insertSnippet('### ${1:$SELECTION}');
  803. }
  804. });
  805. editor.commands.addCommand({
  806. name: 'head_4',
  807. bindKey: {win: "Ctrl-Shift-4", "mac": "Cmd-Shift-4"},
  808. exec: function(editor) {
  809. editor.insertSnippet('#### ${1:$SELECTION}');
  810. }
  811. });
  812. editor.commands.addCommand({
  813. name: 'link',
  814. bindKey: {win: "Ctrl-K", "mac": "Cmd-K"},
  815. exec: function(editor) {
  816. editor.insertSnippet('[${1:$SELECTION}](url)');
  817. }
  818. });
  819. editor.commands.addCommand({
  820. name: 'code',
  821. bindKey: {win: "Ctrl-`", "mac": "Cmd-`"},
  822. exec: function(editor) {
  823. editor.insertSnippet('```${1:$SELECTION}```')
  824. }
  825. });
  826. if (live_preview === undefined || live_preview === true) {
  827. let textarea = $('#'+content_anchor);
  828. // Remove any previous event handler
  829. editor.getSession().off("change");
  830. editor.getSession().on("change", function () {
  831. if (onchange_callback !== undefined && onchange_callback !== null) {
  832. onchange_callback();
  833. }
  834. textarea.text(editor.getSession().getValue());
  835. let target = document.getElementById(target_anchor);
  836. let converter = get_showdown_convert();
  837. let html = converter.makeHtml(editor.getSession().getValue());
  838. target.innerHTML = do_md_filter_xss(html);
  839. });
  840. textarea.text(editor.getSession().getValue());
  841. let target = document.getElementById(target_anchor);
  842. let converter = get_showdown_convert();
  843. let html = converter.makeHtml(editor.getSession().getValue());
  844. target.innerHTML = do_md_filter_xss(html);
  845. }
  846. return editor;
  847. }
  848. function createSanitizeExtensionForImg() {
  849. return [
  850. {
  851. type: 'lang',
  852. regex: /<.*?>/g,
  853. replace: function (match) {
  854. if (match.startsWith('<img')) {
  855. const tempDiv = document.createElement('div');
  856. tempDiv.innerHTML = match;
  857. tempDiv.querySelectorAll('*').forEach(el => {
  858. [...el.attributes].forEach(attr => {
  859. if (attr.name.startsWith('on')) {
  860. el.removeAttribute(attr.name);
  861. }
  862. });
  863. });
  864. return tempDiv.innerHTML;
  865. }
  866. return match;
  867. },
  868. },
  869. ];
  870. }
  871. function get_showdown_convert() {
  872. return new showdown.Converter({
  873. tables: true,
  874. parseImgDimensions: true,
  875. emoji: true,
  876. smoothLivePreview: true,
  877. strikethrough: true,
  878. tasklists: true,
  879. ghCodeBlocks: true,
  880. backslashEscapesHTMLTags: true,
  881. splitAdjacentBlockquotes: true,
  882. extensions: [createSanitizeExtensionForImg, 'bootstrap-tables']
  883. });
  884. }
  885. function do_md_filter_xss(html) {
  886. return filterXSS(html, {
  887. stripIgnoreTag: false,
  888. whiteList: {
  889. i: ['class', "title"],
  890. a: ['href', 'title', 'target'],
  891. img: ['src', 'alt', 'title', 'width', 'height'],
  892. div: ['class'],
  893. p: [],
  894. hr: [],
  895. h1: [], h2: [], h3: [], h4: [], h5: [], h6: [],
  896. ul: [], ol: [], li: [],
  897. code: [], pre: [], em: [], strong: [],
  898. blockquote: [], del: [],
  899. input: ['type', 'checked', 'disabled', 'class'],
  900. table: ['class'], thead: [], tbody: [], tr: [], th: [], td: [], br: []
  901. },
  902. onTagAttr: function (tag, name, value, isWhiteAttr) {
  903. if (tag === "i" && name === "class") {
  904. if (iClassWhiteList.indexOf(value) === -1) {
  905. return false;
  906. } else {
  907. return name + '="' + value + '"';
  908. }
  909. }
  910. }
  911. });
  912. }
  913. const avatarCache = {};
  914. function get_avatar_initials(name, small, onClickFunction, xsmall) {
  915. let av_size = small ? 'avatar-sm' : 'avatar';
  916. av_size = xsmall ? 'avatar-xs' : av_size;
  917. const onClick = onClickFunction ? `onclick="${onClickFunction}"` : '';
  918. if (avatarCache[name] && avatarCache[name][small ? 'small' : 'large']) {
  919. return `<div class="avatar ${av_size}" title="${name}" ${onClick}>
  920. ${avatarCache[name][small ? 'small' : 'large']}
  921. </div>`;
  922. }
  923. // Remove any trailing or leading spaces
  924. name = name.trim();
  925. const initial = name.split(' ');
  926. let snum;
  927. if (initial.length > 1 && initial[1][0] !== undefined) {
  928. snum = initial[0][0].charCodeAt(0) + initial[1][0].charCodeAt(0);
  929. } else {
  930. snum = initial[0][0].charCodeAt(0);
  931. }
  932. const initials = initial.map(i => i[0] ? i[0].toUpperCase(): '').join('');
  933. const avatarColor = get_avatar_color(snum);
  934. const avatarHTMLin = `<span class="avatar-title avatar-iris rounded-circle" style="background-color:${avatarColor}; cursor:pointer;">${initials}</span>`
  935. const avatarHTMLout = `<div class="avatar ${av_size}" title="${name}" ${onClick}>
  936. ${avatarHTMLin}
  937. </div>`;
  938. if (!avatarCache[name]) {
  939. avatarCache[name] = {};
  940. }
  941. avatarCache[name][small ? 'small' : 'large'] = avatarHTMLin;
  942. return avatarHTMLout;
  943. }
  944. function get_avatar_color(snum) {
  945. const hue = snum * 137.508 % 360; // Use the golden angle for more distinct colors
  946. const saturation = 40 + (snum % 20); // Saturation range: 40-60
  947. const lightness = 55 + (snum % 10); // Lightness range: 70-80
  948. return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
  949. }
  950. function edit_inner_editor(btn_id, container_id, ctrd_id) {
  951. $('#'+container_id).toggle();
  952. if ($('#'+container_id).is(':visible')) {
  953. $('#'+btn_id).show(100);
  954. $('#'+ctrd_id).removeClass('col-md-12').addClass('col-md-6');
  955. } else {
  956. $('#'+btn_id).hide(100);
  957. $('#'+ctrd_id).removeClass('col-md-6').addClass('col-md-12');
  958. }
  959. return false;
  960. }
  961. function get_editor_headers(editor_instance, save, edition_btn) {
  962. var save_html = `<div class="btn btn-sm btn-light mr-1 " title="CTRL-S" id="last_saved" onclick="${save}( this );"><i class="fa-solid fa-file-circle-check"></i></div>`;
  963. if (save === undefined || save === null) {
  964. save_html = '';
  965. }
  966. header = `
  967. ${save_html}
  968. <div class="btn btn-sm btn-light mr-1 " title="CTRL-B" onclick="${editor_instance}.insertSnippet`+"('**${1:$SELECTION}**');"+`${editor_instance}.focus();"><i class="fa-solid fa-bold"></i></div>
  969. <div class="btn btn-sm btn-light mr-1" title="CTRL-I" onclick="${editor_instance}.insertSnippet`+"('*${1:$SELECTION}*');"+`${editor_instance}.focus();"><i class="fa-solid fa-italic"></i></div>
  970. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-1" onclick="${editor_instance}.insertSnippet`+"('# ${1:$SELECTION}');"+`${editor_instance}.focus();">H1</div>
  971. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-2" onclick="${editor_instance}.insertSnippet`+"('## ${1:$SELECTION}')"+`;${editor_instance}.focus();">H2</div>
  972. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-3" onclick="${editor_instance}.insertSnippet`+"('### ${1:$SELECTION}');"+`${editor_instance}.focus();">H3</div>
  973. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-4" onclick="${editor_instance}.insertSnippet`+"('#### ${1:$SELECTION}');"+`${editor_instance}.focus();">H4</div>
  974. <div class="btn btn-sm btn-light mr-1" title="CTRL+\`" onclick="${editor_instance}.insertSnippet`+"('```${1:$SELECTION}```');"+`${editor_instance}.focus();"><i class="fa-solid fa-code"></i></div>
  975. <div class="btn btn-sm btn-light mr-1" title="CTRL-K" onclick="${editor_instance}.insertSnippet`+"('[${1:$SELECTION}](URL)');"+`${editor_instance}.focus();"><i class="fa-solid fa-link"></i></div>
  976. <div class="btn btn-sm btn-light mr-1" title="Insert table" onclick="${editor_instance}.insertSnippet`+"('|\\t|\\t|\\t|\\n|--|--|--|\\n|\\t|\\t|\\t|\\n|\\t|\\t|\\t|');"+`${editor_instance}.focus();"><i class="fa-solid fa-table"></i></div>
  977. <div class="btn btn-sm btn-light mr-1" title="Insert bullet list" onclick="${editor_instance}.insertSnippet`+"('\\n- \\n- \\n- ');"+`${editor_instance}.focus();"><i class="fa-solid fa-list"></i></div>
  978. <div class="btn btn-sm btn-light mr-1" title="Insert numbered list" onclick="${editor_instance}.insertSnippet`+"('\\n1. a \\n2. b \\n3. c ');"+`${editor_instance}.focus();"><i class="fa-solid fa-list-ol"></i></div>
  979. <div class="btn btn-sm btn-transparent mr-1" title="Help" onclick="get_md_helper_modal();"><i class="fa-solid fa-question-circle"></i></div>
  980. `
  981. return header;
  982. }
  983. function goto_case_number() {
  984. case_id = $('#goto_case_number_input').val();
  985. if (case_id === '') {
  986. return;
  987. }
  988. if (isNaN(case_id)) {
  989. return;
  990. }
  991. get_request_api(`/api/v2/cases/${case_id}`, true, null, case_id)
  992. .done(function(data, textStatus) {
  993. if (textStatus !== 'success') {
  994. notify_error('Operation failed');
  995. return;
  996. }
  997. var url = new window.URL(document.location);
  998. url.searchParams.set("cid", case_id);
  999. window.location.href = url.href;
  1000. });
  1001. }
  1002. function load_menu_mod_options(data_type, table, deletion_fn, additionalOptions = []) {
  1003. var actionOptions = {
  1004. classes: [],
  1005. contextMenu: {
  1006. enabled: true,
  1007. isMulti: true,
  1008. xoffset: -10,
  1009. yoffset: -10,
  1010. headerRenderer: function (rows) {
  1011. if (rows.length > 1) {
  1012. return rows.length + ' items selected';
  1013. } else {
  1014. let row = rows[0];
  1015. return 'Quick action';
  1016. }
  1017. },
  1018. },
  1019. buttonList: {
  1020. enabled: false,
  1021. },
  1022. deselectAfterAction: true,
  1023. items: [],
  1024. };
  1025. let datatype_map = {
  1026. 'task': 'tasks',
  1027. 'ioc': 'ioc',
  1028. 'evidence': 'evidences',
  1029. 'note': 'notes',
  1030. 'asset': 'assets',
  1031. 'event': 'timeline/events'
  1032. };
  1033. get_request_api("/dim/hooks/options/"+ data_type +"/list")
  1034. .done((data) => {
  1035. if (api_request_failed(data)) {
  1036. return;
  1037. }
  1038. if (data.data != null) {
  1039. jsdata = data.data;
  1040. actionOptions.items.push({
  1041. type: 'option',
  1042. title: 'Comment',
  1043. multi: false,
  1044. iconClass: 'fas fa-comments',
  1045. action: function(rows){
  1046. row = rows[0];
  1047. if (data_type in datatype_map) {
  1048. comment_element(get_row_id(row), datatype_map[data_type]);
  1049. }
  1050. }
  1051. });
  1052. actionOptions.items.push({
  1053. type: 'option',
  1054. title: 'Markdown Link',
  1055. multi: false,
  1056. iconClass: 'fa-brands fa-markdown',
  1057. action: function(rows){
  1058. row = rows[0];
  1059. copy_object_link_md(data_type, get_row_id(row));
  1060. }
  1061. });
  1062. actionOptions.items.push({
  1063. type: 'option',
  1064. title: 'Copy',
  1065. multi: false,
  1066. iconClass: 'fa-regular fa-copy',
  1067. action: function(rows){
  1068. row = rows[0];
  1069. copy_text_clipboard(get_row_value(row));
  1070. }
  1071. });
  1072. actionOptions.items.push({
  1073. type: 'divider'
  1074. });
  1075. jdata_menu_options = jsdata;
  1076. additionalOptions.forEach(option => {
  1077. actionOptions.items.push(option);
  1078. });
  1079. actionOptions.items.push({
  1080. type: 'divider'
  1081. });
  1082. for (let option in jsdata) {
  1083. let opt = jsdata[option];
  1084. actionOptions.items.push({
  1085. type: 'option',
  1086. title: opt.manual_hook_ui_name,
  1087. multi: true,
  1088. multiTitle: opt.manual_hook_ui_name,
  1089. iconClass: 'fas fa-rocket',
  1090. contextMenuClasses: ['text-dark'],
  1091. action: function(rows, de, ke) {
  1092. init_module_processing_wrap(rows, data_type, de[0].outerText);
  1093. },
  1094. });
  1095. }
  1096. if (deletion_fn !== undefined) {
  1097. actionOptions.items.push({
  1098. type: 'divider',
  1099. });
  1100. actionOptions.items.push({
  1101. type: 'option',
  1102. title: 'Delete',
  1103. multi: false,
  1104. iconClass: 'fas fa-trash',
  1105. contextMenuClasses: ['text-danger'],
  1106. action: function(rows){
  1107. row = rows[0];
  1108. deletion_fn(get_row_id(row));
  1109. }
  1110. });
  1111. }
  1112. let tableActions = table.contextualActions(actionOptions);
  1113. tableActions.update();
  1114. }
  1115. });
  1116. }
  1117. function get_custom_attributes_fields() {
  1118. values = Object();
  1119. has_error = [];
  1120. $("input[id^='inpstd_']").each(function (i, el) {
  1121. tab = $(el).attr('data-ref-tab');
  1122. field = $(el).attr('data-attr-for');
  1123. if (!(tab in values)) { values[tab] = {} };
  1124. values[tab][field] = $(el).val();
  1125. if ($(el).prop('required') && !values[tab][field]) {
  1126. $(el).parent().addClass('has-error');
  1127. has_error.push(field);
  1128. } else {
  1129. $(el).parent().removeClass('has-error');
  1130. }
  1131. })
  1132. $("textarea[id^='inpstd_']").each(function (i, el) {
  1133. tab = $(el).attr('data-ref-tab');
  1134. field = $(el).attr('data-attr-for');
  1135. if (!(tab in values)) { values[tab] = {} };
  1136. values[tab][field] = $(el).val();
  1137. if ($(el).prop('required') && !values[tab][field]) {
  1138. $(el).parent().addClass('has-error');
  1139. has_error.push(field);
  1140. } else {
  1141. $(el).parent().removeClass('has-error');
  1142. }
  1143. })
  1144. $("input[id^='inpchk_']").each(function (i, el) {
  1145. tab = $(el).attr('data-ref-tab');
  1146. field = $(el).attr('data-attr-for');
  1147. if (!(tab in values)) { values[tab] = {} };
  1148. values[tab][field] = $(el).is(':checked');
  1149. })
  1150. $("select[id^='inpselect_']").each(function (i, el) {
  1151. tab = $(el).attr('data-ref-tab');
  1152. field = $(el).attr('data-attr-for');
  1153. if (!(tab in values)) { values[tab] = {} };
  1154. values[tab][field] = $(el).val();
  1155. if ($(el).prop('required') && !values[tab][field]) {
  1156. $(el).parent().addClass('has-error');
  1157. has_error.push(field);
  1158. } else {
  1159. $(el).parent().removeClass('has-error');
  1160. }
  1161. })
  1162. if (has_error.length > 0) {
  1163. msg = 'Missing required fields: <br/>';
  1164. for (field in has_error) {
  1165. msg += ' - ' + has_error[field] + '<br/>';
  1166. }
  1167. notify_error(msg);
  1168. }
  1169. return [has_error, values];
  1170. }
  1171. function update_time() {
  1172. $('#current_date').text((new Date()).toLocaleString());
  1173. }
  1174. function formatTime(in_, format) {
  1175. if (typeof(in_) === typeof(1)){
  1176. let date = new Date(Math.floor(in_) * 1000);
  1177. return date.toLocaleString(undefined, format);
  1178. } else if (typeof(in_) === typeof('')) {
  1179. let date = new Date(in_);
  1180. return date.toLocaleString(undefined, format);
  1181. }
  1182. }
  1183. function download_file(filename, contentType, data) {
  1184. var element = document.createElement('a');
  1185. element.setAttribute('href', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(data));
  1186. element.setAttribute('download', filename);
  1187. element.style.display = 'none';
  1188. document.body.appendChild(element);
  1189. element.click();
  1190. document.body.removeChild(element);
  1191. }
  1192. function toggle_focus_mode() {
  1193. class_a = "bg-focus-gradient"
  1194. $(".modal-case-focus").each(function (i, el) {
  1195. if ($(el).hasClass( class_a )) {
  1196. $(el).removeClass(class_a, 1000);
  1197. } else {
  1198. $(el).addClass(class_a, 1000);
  1199. }
  1200. });
  1201. }
  1202. function modal_maximize() {
  1203. id = $('#minimized_modal_box').data('target-id');
  1204. $("#" + id).modal("show");
  1205. $("#minimized_modal_box").hide();
  1206. }
  1207. function modal_minimized(id, title) {
  1208. $("#minimized_modal_title").text(title);
  1209. $('#minimized_modal_box').data('target-id',id);
  1210. $("#minimized_modal_box").show();
  1211. $("#" + id).modal("hide");
  1212. }
  1213. function hide_minimized_modal_box() {
  1214. $("#minimized_modal_box").hide();
  1215. $("#minimized_modal_title").text('');
  1216. $('#minimized_modal_box').data('target-id','');
  1217. }
  1218. function hide_table_search_input(columns) {
  1219. for (i=0; i<columns.length; i++) {
  1220. if (columns[i]) {
  1221. $('.filters th:eq(' + i + ')' ).show();
  1222. } else {
  1223. $('.filters th:eq(' + i + ')' ).hide();
  1224. }
  1225. }
  1226. }
  1227. function load_add_case() {
  1228. // Dynamically load the modal
  1229. $('#modal_add_case_content').load('/manage/cases/add/modal', function (response, status, xhr) {
  1230. if (status !== "success") {
  1231. ajax_notify_error(xhr, '/case/add');
  1232. return false;
  1233. }
  1234. $('#case_customer').selectpicker({
  1235. liveSearch: true,
  1236. title: "Select customer *",
  1237. style: "btn-outline-white",
  1238. size: 8
  1239. });
  1240. $('#case_template_id').selectpicker({
  1241. liveSearch: true,
  1242. title: "Select case template",
  1243. style: "btn-outline-white",
  1244. size: 8
  1245. });
  1246. $('#case_template_id').prepend(new Option('', ''));
  1247. $('#classification_id').selectpicker({
  1248. liveSearch: true,
  1249. title: "Select classification",
  1250. style: "btn-outline-white",
  1251. size: 8
  1252. });
  1253. $('#classification_id').prepend(new Option('', ''));
  1254. $('#modal_add_case').modal({show:true});
  1255. });
  1256. }
  1257. /* Submit event handler for new case */
  1258. function submit_new_case() {
  1259. let data_sent = $('form#form_new_case').serializeObject();
  1260. let ret = get_custom_attributes_fields();
  1261. let has_error = ret[0].length > 0;
  1262. let attributes = ret[1];
  1263. if (has_error){return false;}
  1264. data_sent['custom_attributes'] = attributes;
  1265. send_add_case(data_sent);
  1266. return false;
  1267. }
  1268. function set_suggest_tags(anchor_id) {
  1269. $(`#${anchor_id}`).amsifySuggestags({
  1270. suggestionsAction : {
  1271. url: '/manage/tags/suggest',
  1272. method: 'GET',
  1273. timeout: -1,
  1274. minChars: 2,
  1275. minChange: -1,
  1276. delay: 100,
  1277. type: 'GET',
  1278. dataType: null
  1279. },
  1280. printValues: false
  1281. });
  1282. }
  1283. function send_add_case(data_sent) {
  1284. post_request_api('/api/v2/cases', JSON.stringify(data_sent), true, function () {
  1285. $('#submit_new_case_btn').text('Checking data..')
  1286. .attr("disabled", true)
  1287. .removeClass('bt-outline-success')
  1288. .addClass('btn-success', 'text-dark');
  1289. })
  1290. .done((data, textStatus) => {
  1291. if (textStatus === 'success') {
  1292. let case_id = data.case_id;
  1293. swal("That's done !",
  1294. "Case has been successfully created",
  1295. "success",
  1296. {
  1297. buttons: {
  1298. dash: {
  1299. text: "Go to dashboard",
  1300. value: "dash",
  1301. color: '#d33'
  1302. },
  1303. go_case: {
  1304. text: "Switch to newly created case",
  1305. value: "go_case"
  1306. }
  1307. }
  1308. }
  1309. ).then((value) => {
  1310. switch (value) {
  1311. case "dash":
  1312. window.location.replace("/dashboard" + case_param());
  1313. break;
  1314. case 'go_case':
  1315. window.location.replace("/case?cid=" + case_id);
  1316. default:
  1317. window.location.replace("/case?cid=" + case_id);
  1318. }
  1319. });
  1320. }
  1321. })
  1322. .always(() => {
  1323. $('#submit_new_case_btn')
  1324. .attr("disabled", false)
  1325. .addClass('bt-outline-success')
  1326. .removeClass('btn-success', 'text-dark');
  1327. })
  1328. .fail(() => {
  1329. $('#submit_new_case_btn').text('Save');
  1330. })
  1331. }
  1332. function load_context_switcher() {
  1333. var options = {
  1334. ajax: {
  1335. url: '/context/search-cases'+ case_param(),
  1336. type: 'GET',
  1337. dataType: 'json'
  1338. },
  1339. locale: {
  1340. emptyTitle: 'Select and Begin Typing',
  1341. statusInitialized: '',
  1342. },
  1343. minLength: 0,
  1344. clearOnEmpty: false,
  1345. emptyRequest: true,
  1346. preprocessData: function (data) {
  1347. return context_data_parser(data);
  1348. },
  1349. preserveSelected: false
  1350. };
  1351. get_request_api('/context/search-cases')
  1352. .done((data) => {
  1353. context_data_parser(data);
  1354. $('#user_context').ajaxSelectPicker(options);
  1355. });
  1356. }
  1357. function context_data_parser(data, fire_modal = true) {
  1358. if (api_request_failed(data)) {
  1359. return;
  1360. }
  1361. $('#user_context').empty();
  1362. $('#user_context').append('<optgroup label="Open" id="switch_case_opened_opt"></optgroup>');
  1363. $('#user_context').append('<optgroup label="Closed" id="switch_case_closed_opt"></optgroup>');
  1364. ocs = data.data;
  1365. ret_data = [];
  1366. for (index in ocs) {
  1367. case_name = sanitizeHTML(ocs[index].name);
  1368. cs_name = sanitizeHTML(ocs[index].customer_name);
  1369. ret_data.push({
  1370. 'value': ocs[index].case_id,
  1371. 'text': `${case_name} (${cs_name}) ${ocs[index].access}`
  1372. });
  1373. if (ocs[index].close_date != null) {
  1374. $('#switch_case_closed_opt').append(`<option value="${ocs[index].case_id}">${case_name} (${cs_name}) ${ocs[index].access}</option>`);
  1375. } else {
  1376. $('#switch_case_opened_opt').append(`<option value="${ocs[index].case_id}">${case_name} (${cs_name}) ${ocs[index].access}</option>`)
  1377. }
  1378. }
  1379. if (fire_modal) {
  1380. $('#modal_switch_context').modal("show");
  1381. }
  1382. $('#user_context').selectpicker('refresh');
  1383. $('#user_context').selectpicker('val', get_caseid());
  1384. return ret_data;
  1385. }
  1386. function focus_on_input_chg_case(){
  1387. $('#goto_case_number_input').focus();
  1388. $('#goto_case_number_input').keydown(function(event) {
  1389. if (event.keyCode == 13) {
  1390. goto_case_number();
  1391. return false;
  1392. }
  1393. });
  1394. }
  1395. function get_md_helper_modal() {
  1396. $('#modal_md_helper').load('/case/md-helper?cid=' + get_caseid(), function (response, status, xhr) {
  1397. if (status !== "success") {
  1398. ajax_notify_error(xhr, '/case/md-helper?cid=' + get_caseid());
  1399. return false;
  1400. }
  1401. $('#shortcutModal').modal("show");
  1402. });
  1403. }
  1404. function split_bool(split_str) {
  1405. and_split = split_str.split(' AND ');
  1406. if (and_split[0]) {
  1407. return and_split[0];
  1408. }
  1409. return null;
  1410. }
  1411. function random_filename(length) {
  1412. var filename = '';
  1413. var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  1414. var char_length = characters.length;
  1415. for ( var i = 0; i < length; i++ ) {
  1416. filename += characters.charAt(Math.random() * 1000 % char_length);
  1417. }
  1418. return filename;
  1419. }
  1420. function createPagination(currentPage, totalPages, per_page, callback, paginationContainersNodes) {
  1421. const maxPagesToShow = 5;
  1422. const paginationContainers = $(paginationContainersNodes);
  1423. if (totalPages === 1 || totalPages === 0) {
  1424. paginationContainers.html('');
  1425. return;
  1426. }
  1427. paginationContainers.each(function() {
  1428. const paginationContainer = $(this);
  1429. paginationContainer.html('');
  1430. const startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2));
  1431. const endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
  1432. // Add First page button
  1433. if (totalPages > maxPagesToShow) {
  1434. if (currentPage !== 1 && maxPagesToShow / 2 + 1 < currentPage) {
  1435. const firstItem = $('<li>', {class: 'page-item'}).appendTo(paginationContainer);
  1436. $('<a>', {
  1437. href: `javascript:${callback}(1, ${per_page},{}, true)`,
  1438. text: 'First page',
  1439. class: 'page-link',
  1440. }).appendTo(firstItem);
  1441. }
  1442. }
  1443. // Add Previous button
  1444. if (currentPage !== 1) {
  1445. const prevItem = $('<li>', { class: 'page-item' }).appendTo(paginationContainer);
  1446. $('<a>', {
  1447. href: `javascript:${callback}(${Math.max(1, currentPage - 1)}, ${per_page},{}, true)`,
  1448. text: 'Previous',
  1449. class: 'page-link',
  1450. }).appendTo(prevItem);
  1451. }
  1452. // Add page numbers
  1453. for (let i = startPage; i <= endPage; i++) {
  1454. const pageItem = $('<li>', { class: 'page-item' }).appendTo(paginationContainer);
  1455. if (i === currentPage) {
  1456. pageItem.addClass('active');
  1457. }
  1458. $('<a>', {
  1459. href: `javascript:${callback}(${i}, ${per_page},{}, true)`,
  1460. text: i,
  1461. class: 'page-link',
  1462. }).appendTo(pageItem);
  1463. }
  1464. // Add Next button
  1465. if (currentPage !== totalPages) {
  1466. const nextItem = $('<li>', { class: 'page-item' }).appendTo(paginationContainer);
  1467. $('<a>', {
  1468. href: `javascript:${callback}(${Math.min(totalPages, currentPage + 1)}, ${per_page},{}, true)`,
  1469. text: 'Next',
  1470. class: 'page-link',
  1471. }).appendTo(nextItem);
  1472. }
  1473. if (totalPages > maxPagesToShow) {
  1474. if (currentPage !== totalPages) {
  1475. const lastItem = $('<li>', {class: 'page-item'}).appendTo(paginationContainer);
  1476. $('<a>', {
  1477. href: `javascript:${callback}(${totalPages}, ${per_page},{}, true)`,
  1478. text: 'Last page',
  1479. class: 'page-link',
  1480. }).appendTo(lastItem);
  1481. }
  1482. }
  1483. });
  1484. }
  1485. let userWhoami = JSON.parse(sessionStorage.getItem('userWhoami'));
  1486. function userWhoamiRequest(force = false) {
  1487. if (!userWhoami || force) {
  1488. get_request_api('/user/whoami')
  1489. .done((data) => {
  1490. if (notify_auto_api(data, true)) {
  1491. userWhoami = data.data;
  1492. sessionStorage.setItem('userWhoami', JSON.stringify(userWhoami));
  1493. }
  1494. });
  1495. }
  1496. }
  1497. $('.toggle-sidebar').on('click', function() {
  1498. if ($('.wrapper').hasClass('sidebar_minimize')) {
  1499. $('.wrapper').removeClass('sidebar_minimize');
  1500. get_request_api('/user/mini-sidebar/set/false')
  1501. .then((data) => {
  1502. if (data.success !== 'success') {
  1503. notify_api_request_error(data);
  1504. }
  1505. });
  1506. } else {
  1507. $('.wrapper').addClass('sidebar_minimize');
  1508. get_request_api('/user/mini-sidebar/set/true')
  1509. .then((data) => {
  1510. if (data.success !== 'success') {
  1511. notify_api_request_error(data);
  1512. }
  1513. });
  1514. }
  1515. });
  1516. function do_deletion_prompt(message, force_prompt=false) {
  1517. if (userWhoami.has_deletion_confirmation || force_prompt) {
  1518. return new Promise((resolve, reject) => {
  1519. swal({
  1520. title: "Are you sure?",
  1521. text: message,
  1522. icon: "warning",
  1523. buttons: {
  1524. cancel: {
  1525. text: "Cancel",
  1526. value: false,
  1527. visible: true,
  1528. closeModal: true
  1529. },
  1530. confirm: {
  1531. text: "Confirm",
  1532. value: true
  1533. }
  1534. },
  1535. dangerMode: true
  1536. })
  1537. .then((willDelete) => {
  1538. resolve(willDelete);
  1539. })
  1540. .catch((error) => {
  1541. reject(error);
  1542. });
  1543. });
  1544. } else {
  1545. return new Promise((resolve, reject) => {
  1546. resolve(true);
  1547. });
  1548. }
  1549. }
  1550. function toBinary64(string) {
  1551. const codeUnits = new Uint16Array(string.length);
  1552. for (let i = 0; i < codeUnits.length; i++) {
  1553. codeUnits[i] = string.charCodeAt(i);
  1554. }
  1555. return btoa(String.fromCharCode(...new Uint8Array(codeUnits.buffer)));
  1556. }
  1557. function fromBinary64(encoded) {
  1558. const binary = atob(encoded);
  1559. const bytes = new Uint8Array(binary.length);
  1560. for (let i = 0; i < bytes.length; i++) {
  1561. bytes[i] = binary.charCodeAt(i);
  1562. }
  1563. return String.fromCharCode(...new Uint16Array(bytes.buffer));
  1564. }
  1565. $(document).ready(function(){
  1566. notify_redirect();
  1567. update_time();
  1568. setInterval(function() { update_time(); }, 30000);
  1569. $(function () {
  1570. var current = location.pathname;
  1571. btt = current.split('/')[1];
  1572. if (btt !== 'manage') {
  1573. btt = btt.split('?')[0];
  1574. } else {
  1575. csp = current.split('?')[0].split('/')
  1576. if (csp.length >= 3) {
  1577. csp = csp.splice(0, 3);
  1578. }
  1579. btt = csp.join('/');
  1580. }
  1581. $('#l_nav_tab .nav-item').each(function (k, al) {
  1582. href = $(al).children().attr('href');
  1583. try {
  1584. if (href == "#advanced-nav") {
  1585. $('#advanced-nav .nav-subitem').each(function (i, el) {
  1586. ktt = $(el).children().attr('href').split('?')[0];
  1587. if (ktt === btt) {
  1588. $(el).addClass('active');
  1589. $(al).addClass('active');
  1590. $(al).children().attr('aria-expanded', true);
  1591. $('#advanced-nav').show();
  1592. return false;
  1593. }
  1594. });
  1595. } else if (href.startsWith(btt)){
  1596. $(this).addClass('active');
  1597. return false;
  1598. }else{
  1599. att = "";
  1600. att = href.split('/')[1].split('?')[0];
  1601. }
  1602. } catch {att=""}
  1603. if (att === btt) {
  1604. $(al).addClass('active');
  1605. return false;
  1606. }
  1607. })
  1608. })
  1609. $('#submit_set_context').click(function () {
  1610. var data_sent = new Object();
  1611. data_sent.ctx = $('#user_context').val();
  1612. data_sent.ctx_h = $("#user_context option:selected").text();
  1613. post_request_api(`/context/set?cid=${data_sent.ctx}`, data_sent)
  1614. .done((data) => {
  1615. if (api_request_failed(data)) {
  1616. return true;
  1617. }
  1618. $('#modal_switch_context').modal('hide');
  1619. swal({
  1620. title: 'Context changed successfully',
  1621. text: 'Reloading...',
  1622. icon: 'success',
  1623. timer: 500,
  1624. buttons: false,
  1625. })
  1626. .then(() => {
  1627. var newURL = updateURLParameter(window.location.href, 'cid', data_sent.ctx);
  1628. window.history.replaceState('', '', newURL);
  1629. location.reload();
  1630. })
  1631. });
  1632. });
  1633. $(".rotate").click(function () {
  1634. $(this).toggleClass("down");
  1635. });
  1636. $(function () {
  1637. $('[data-toggle="popover"]').popover({
  1638. trigger: 'focus',
  1639. placement: 'auto',
  1640. container: 'body',
  1641. html: true
  1642. });
  1643. });
  1644. $('.modal-dialog').draggable({
  1645. handle: ".modal-header"
  1646. });
  1647. $('#form_add_tasklog').submit(function () {
  1648. event.preventDefault();
  1649. event.stopImmediatePropagation();
  1650. var data = $('form#form_add_tasklog').serializeObject();
  1651. data['csrf_token'] = $('#csrf_token').val();
  1652. post_request_api('/case/tasklog/add', JSON.stringify(data), true)
  1653. .done(function (data){
  1654. if (notify_auto_api(data)){
  1655. $('#modal_add_tasklog').modal('hide');
  1656. }
  1657. });
  1658. return false;
  1659. });
  1660. var sh_ext = showdown.extension('bootstrap-tables', function () {
  1661. return [{
  1662. type: "output",
  1663. filter: function (html, converter, options) {
  1664. let parser = new DOMParser();
  1665. html = '<div id="fsjpowefjdwe">' + html + '</div>';
  1666. let doc = parser.parseFromString(html, 'text/html');
  1667. let tables = doc.getElementsByTagName('table');
  1668. for (let i = 0; i < tables.length; i++) {
  1669. let table = tables[i];
  1670. table.classList.add('table', 'table-striped', 'table-bordered', 'table-hover', 'table-sm');
  1671. let div = doc.createElement('div');
  1672. div.classList.add('table-responsive');
  1673. table.parentNode.insertBefore(div, table);
  1674. div.appendChild(table);
  1675. }
  1676. let serializer = new XMLSerializer();
  1677. let newHtml = serializer.serializeToString(doc);
  1678. let innerHtml = doc.getElementById('fsjpowefjdwe').innerHTML;
  1679. return innerHtml;
  1680. }
  1681. }];
  1682. });
  1683. userWhoamiRequest();
  1684. });