1. 1 : /* eslint-disable no-shadow */
  2. 2 : /**
  3. 3 : * Met en pause une fonction asynchrone.
  4. 4 : * @param {number} ms
  5. 5 : */
  6. 6 : const delay = (ms) => new Promise((res) => setTimeout(res, ms));
  7. 7 :
  8. 8 : /**
  9. 9 : * Vérifie si une fonction est asynchrone
  10. 10 : * @param {Function} myFunction
  11. 11 : * @returns {boolean}
  12. 12 : */
  13. 13 : const isAsync = (myFunction) => myFunction.constructor.name === 'AsyncFunction';
  14. 14 :
  15. 15 : /**
  16. 16 : * Attend que la fonction soit vrai
  17. 17 : * @param {Function} func
  18. 18 : * @param {number} waitTime ms
  19. 19 : */
  20. 20 : const wait = async function (func, waitTime = 500) {
  21. 21 : while (isAsync(func) ? await func() : func()) {
  22. 22 : await delay(waitTime);
  23. 23 : }
  24. 24 : };
  25. 25 :
  26. 26 : const ping = async function (url, useSSL = true) {
  27. 27 : // //let ip = url;
  28. 28 :
  29. 29 : // var _that = this;
  30. 30 :
  31. 31 : // let img = new Image();
  32. 32 :
  33. 33 : // img.onload = function() {_that.ok = true;};
  34. 34 : // img.onerror = function(e) {_that.ok = false; console.error(e);};
  35. 35 :
  36. 36 : // //let start = new Date().getTime();
  37. 37 : let ssl = useSSL ? 'https' : 'http';
  38. 38 : // img.src = !url.includes("https") && !url.includes("http") ? (ssl + "://" + url) : url;
  39. 39 : // console.log(img.src);
  40. 40 : // let timer = setTimeout(function() { _that.ok = false;}, waitTime*1000);
  41. 41 : // await wait(() => _that.ok === undefined);
  42. 42 : // clearTimeout(timer)
  43. 43 : // return _that.ok;
  44. 44 : let ok;
  45. 45 : try {
  46. 46 : await $.ajax({
  47. 47 : type: 'GET',
  48. 48 : url:
  49. 49 : !url.includes('https') && !url.includes('http')
  50. 50 : ? ssl + '://' + url
  51. 51 : : url,
  52. 52 : success: function (result) {
  53. 53 : ok = true;
  54. 54 : },
  55. 55 : error: function (result) {
  56. 56 : ok = false;
  57. 57 : },
  58. 58 : });
  59. 59 : } catch (error) {
  60. 60 : console.error(error);
  61. 61 : ok = false;
  62. 62 : }
  63. 63 : return ok;
  64. 64 : };
  65. 65 :
  66. 66 : const mceToRcId = function (txt = '') {
  67. 67 : return txt
  68. 68 : .replaceAll('.', '_-P-_')
  69. 69 : .replaceAll('@', "'_-A-_'")
  70. 70 : .replaceAll('%', '_-C-_');
  71. 71 : };
  72. 72 :
  73. 73 : (() => {
  74. 74 : function ureplacer(pmatch) {
  75. 75 : var ret = '';
  76. 76 : pmatch = pmatch.replace(/\,/g, '/');
  77. 77 : var ix = pmatch.substr(1, pmatch.length - 2);
  78. 78 :
  79. 79 : if (ix.length % 4 != 0)
  80. 80 : ix = ix.padEnd(ix.length + 4 - (ix.length % 4), '=');
  81. 81 : try {
  82. 82 : var dx = atob(ix);
  83. 83 : for (var j = 0; j < dx.length; j = j + 2) {
  84. 84 : ret =
  85. 85 : ret +
  86. 86 : String.fromCharCode((dx.charCodeAt(j) << 8) + dx.charCodeAt(j + 1));
  87. 87 : }
  88. 88 : } catch (err) {
  89. 89 : console.log(
  90. 90 : 'Error in decoding foldername IMAP UTF7, sending empty string back',
  91. 91 : );
  92. 92 : console.log(err);
  93. 93 : ret = '';
  94. 94 : }
  95. 95 : return ret;
  96. 96 : }
  97. 97 :
  98. 98 : function breplacer(umatch) {
  99. 99 : var bst = '';
  100. 100 : for (var i = 0; i < umatch.length; i++) {
  101. 101 : var f = umatch.charCodeAt(i);
  102. 102 : bst = bst + String.fromCharCode(f >> 8) + String.fromCharCode(f & 255);
  103. 103 : }
  104. 104 :
  105. 105 : try {
  106. 106 : bst = '&' + btoa(bst).replace(/\//g, ',').replace(/=+/, '') + '-';
  107. 107 : } catch (err) {
  108. 108 : console.log(
  109. 109 : 'Error in encoding foldername IMAP UTF7, sending empty string back',
  110. 110 : );
  111. 111 : console.log(err);
  112. 112 : bst = '';
  113. 113 : }
  114. 114 : return bst;
  115. 115 : }
  116. 116 :
  117. 117 : function decode_imap_utf7(mstring) {
  118. 118 : var stm = new RegExp(/(\&[A-Za-z0-9\+\,]+\-)/, 'g');
  119. 119 : return mstring.replace(stm, ureplacer).replace('&-', '&');
  120. 120 : }
  121. 121 :
  122. 122 : function encode_imap_utf7(ustring) {
  123. 123 : ustring = ustring.replace(/\/|\~|\\/g, '');
  124. 124 : var vgm = new RegExp(/([^\x20-\x7e]+)/, 'g');
  125. 125 : return ustring.replace('&', '&-').replace(vgm, breplacer);
  126. 126 : }
  127. 127 :
  128. 128 : function setCookie(cname, cvalue, exdays) {
  129. 129 : const d = new Date();
  130. 130 : d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
  131. 131 : let expires = 'expires=' + d.toUTCString();
  132. 132 : document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/';
  133. 133 : }
  134. 134 :
  135. 135 : window.decode_imap_utf7 = decode_imap_utf7;
  136. 136 :
  137. 137 : window.melSetCookie = setCookie;
  138. 138 :
  139. 139 : window.getCookie = (name) => {
  140. 140 : return rcmail.get_cookie(name);
  141. 141 : };
  142. 142 :
  143. 143 : window.removeCookie = (name) => {
  144. 144 : setCookie(name, '', -5);
  145. 145 : };
  146. 146 : })();
  147. 147 : /**
  148. 148 : * Lien du chargement des évènements d'un calendrier.
  149. 149 : */
  150. 150 : const ev_calendar_url = '?_task=calendar&_action=load_events';
  151. 151 :
  152. 152 : /**
  153. 153 : * Liste des différents données constantes utile pour le plugin "mel_metapage".
  154. 154 : */
  155. 155 : const mel_metapage = {
  156. 156 : /**
  157. 157 : * Liste des différents évènements.
  158. 158 : */
  159. 159 : EventListeners: {
  160. 160 : /**
  161. 161 : * Lorsque le calendrier est mis à jours.
  162. 162 : */
  163. 163 : calendar_updated: new EventListenerDatas('mel_metapage.calendar_updated'),
  164. 164 : /**
  165. 165 : * Lorsque les tâches sont mises à jours.
  166. 166 : */
  167. 167 : tasks_updated: new EventListenerDatas('mel_metapage.tasks_updated'),
  168. 168 : /**
  169. 169 : * Lorsque les mails sont mis à jours.
  170. 170 : */
  171. 171 : mails_updated: new EventListenerDatas('mel_metapage.mails_updated'),
  172. 172 : /**
  173. 173 : * Lorsque le stockage est mis à jours.
  174. 174 : */
  175. 175 : wsp_stockage_updated: new EventListenerDatas(
  176. 176 : 'mel_metapage.wsp_stockage_updated',
  177. 177 : ),
  178. 178 : workspaces_updated: new EventListenerDatas('workspace.updated'),
  179. 179 : },
  180. 180 : /**
  181. 181 : * Différents clés de stockage local.
  182. 182 : */
  183. 183 : Storage: {
  184. 184 : unexist: Symbol('unexist'),
  185. 185 : exists(val) {
  186. 186 : return (val ?? this.unexist) !== this.unexist;
  187. 187 : },
  188. 188 : /**
  189. 189 : *
  190. 190 : * @returns {MelDataStore}
  191. 191 : */
  192. 192 : _getDataStore() {
  193. 193 : let self = (top ?? window).mel_metapage.Storage;
  194. 194 :
  195. 195 : if (!self._getDataStore.datastoreobject) {
  196. 196 : const current_user_key = `bnum.${rcmail.env.username}`;
  197. 197 : self._getDataStore.datastoreobject = new MelDataStore(
  198. 198 : current_user_key,
  199. 199 : {},
  200. 200 : );
  201. 201 :
  202. 202 : if (rcmail.env.keep_login) {
  203. 203 : const keys = Object.keys(localStorage);
  204. 204 :
  205. 205 : for (let index = 0, len = keys.length; index < len; ++index) {
  206. 206 : const key = keys[index];
  207. 207 :
  208. 208 : if (key !== current_user_key && key.includes('bnum')) {
  209. 209 : localStorage.removeItem(key);
  210. 210 : }
  211. 211 : }
  212. 212 : }
  213. 213 : }
  214. 214 :
  215. 215 : return self._getDataStore.datastoreobject;
  216. 216 : },
  217. 217 : /**
  218. 218 : * Récupère une donnée depuis le stockage local.
  219. 219 : * @param {string} key Clé de la donnée à récupérer.
  220. 220 : */
  221. 221 : get(key, _default = null) {
  222. 222 : let self = mel_metapage.Storage;
  223. 223 : try {
  224. 224 : return self._getDataStore().get(key) ?? _default;
  225. 225 : } catch (error) {
  226. 226 : console.error(error);
  227. 227 : return self.unexist;
  228. 228 : }
  229. 229 : },
  230. 230 : /**
  231. 231 : * Ajoute ou modifie une donnée dans le stockage local.
  232. 232 : * @param {string} key Clé de la donnée pour la retrouver.
  233. 233 : * @param {*} item Donnée à sauvegarder.
  234. 234 : */
  235. 235 : set(key, item, stringify = true) {
  236. 236 : this._getDataStore().set(key, item);
  237. 237 : this.setStoreChange(key, item);
  238. 238 : },
  239. 239 : /**
  240. 240 : * Supprime une donnée dans le stockage local.
  241. 241 : * @param {string} key Clé de la donnée à supprimer.
  242. 242 : */
  243. 243 : remove(key) {
  244. 244 : this._getDataStore().remove(key);
  245. 245 : this.setStoreChange(key, undefined);
  246. 246 : },
  247. 247 : setStoreChange(key, item) {
  248. 248 : if ((top ?? window).rcmail !== undefined)
  249. 249 : (top ?? window).rcmail.triggerEvent('storage.change', { key, item });
  250. 250 :
  251. 251 : (top ?? window).$('iframe.mm-frame').each((i, e) => {
  252. 252 : try {
  253. 253 : e.contentWindow.rcmail.triggerEvent('storage.change', { key, item });
  254. 254 : } catch (error) {}
  255. 255 : });
  256. 256 : },
  257. 257 : getAppStorageSize() {
  258. 258 : return this._getDataStore().getSize();
  259. 259 : },
  260. 260 : check(storage = null) {
  261. 261 : if (storage === null) {
  262. 262 : let items = [];
  263. 263 : for (const key in mel_metapage.Storage) {
  264. 264 : const element = mel_metapage.Storage[key];
  265. 265 : if (element !== undefined) {
  266. 266 : items.push(mel_metapage.Storage.check_day(key));
  267. 267 : }
  268. 268 : }
  269. 269 :
  270. 270 : return {
  271. 271 : items: items,
  272. 272 : wait: async function () {
  273. 273 : for (let index = 0; index < this.items.length; ++index) {
  274. 274 : const element = this.items[index];
  275. 275 : if (element.wait !== undefined) await element.wait();
  276. 276 : }
  277. 277 : },
  278. 278 : };
  279. 279 : } else {
  280. 280 : const update_element = (update_key, day_key) => {
  281. 281 : let item = {
  282. 282 : wait: async () => {
  283. 283 : return mel_metapage.Storage.get(update_key);
  284. 284 : },
  285. 285 : };
  286. 286 : let update = false;
  287. 287 : if (
  288. 288 : !update &&
  289. 289 : moment(mel_metapage.Storage.get(day_key)).format('DD/MM/YYYY') !==
  290. 290 : moment().format('DD/MM/YYYY')
  291. 291 : )
  292. 292 : update = true;
  293. 293 : if (!update && mel_metapage.Storage.get(update_key) === null)
  294. 294 : update = true;
  295. 295 : if (update) {
  296. 296 : mel_metapage.Storage.remove(update_key);
  297. 297 : workspaces.sync.PostToParent({
  298. 298 : exec: 'rcmail.mel_metapage_fn.tasks_updated()',
  299. 299 : child: false,
  300. 300 : });
  301. 301 : item.wait = async () => {
  302. 302 : await wait(() => mel_metapage.Storage.get(update_key) === null);
  303. 303 : return mel_metapage.Storage.get(update_key);
  304. 304 : };
  305. 305 : }
  306. 306 : return item;
  307. 307 : };
  308. 308 :
  309. 309 : switch (storage) {
  310. 310 : case mel_metapage.Storage.other_tasks:
  311. 311 : case mel_metapage.Storage.tasks:
  312. 312 : return update_element(
  313. 313 : storage,
  314. 314 : mel_metapage.Storage.last_task_update,
  315. 315 : );
  316. 316 : case mel_metapage.Storage.calendar_all_events:
  317. 317 : case mel_metapage.Storage.calendar:
  318. 318 : return update_element(
  319. 319 : storage,
  320. 320 : mel_metapage.Storage.last_calendar_update,
  321. 321 : );
  322. 322 : default:
  323. 323 : return item;
  324. 324 : }
  325. 325 : }
  326. 326 : },
  327. 327 : /**
  328. 328 : * Clé des données du calendrier.
  329. 329 : */
  330. 330 : calendar_all_events: CONST_STORAGE_KEY_ALL_EVENTS,
  331. 331 : calendar: 'mel_metapage.calendar',
  332. 332 : calendar_by_days: 'mel_metapage.calendars.by_days',
  333. 333 : calendars_number_wainting: 'mel_metapage.calendars.by_days.waiting',
  334. 334 : /**
  335. 335 : * Clé des données des tâches.
  336. 336 : */
  337. 337 : tasks: 'mel_metapage.tasks',
  338. 338 : other_tasks: 'mel_metapage.tasks.others',
  339. 339 : other_tasks_count: 'mel_metapage.tasks.others.count',
  340. 340 : /**
  341. 341 : * Clé du nombre de mail non lus.
  342. 342 : */
  343. 343 : mail: 'mel_metapage.mail.count',
  344. 344 : wsp_mail: 'mel_metapage.wsp.mails',
  345. 345 : last_calendar_update: 'mel_metapage.calendar.last_update_2',
  346. 346 : last_task_update: 'mel_metapage.tasks.last_update',
  347. 347 : ariane: 'ariane_datas',
  348. 348 : wait_frame_loading: 'mel_metapage.wait_frame_loading',
  349. 349 : wait_frame_waiting: 'waiting...',
  350. 350 : wait_frame_loaded: 'loaded',
  351. 351 : wait_call_loading: 'mel_metapage.call.loading',
  352. 352 : color_mode: 'colorMode',
  353. 353 : title_workspaces: 'workspaces.title',
  354. 354 : },
  355. 355 : /**
  356. 356 : * Liste des symboles.
  357. 357 : */
  358. 358 : Symbols: {
  359. 359 : /**
  360. 360 : * Symboles du plugin "My_Day".
  361. 361 : */
  362. 362 : my_day: {
  363. 363 : /**
  364. 364 : * Symbole "Calendrier", est utilisé pour savoir si il faut mettre à jours uniquement les évènements ou non.
  365. 365 : */
  366. 366 : calendar: Symbol('calendar'),
  367. 367 : /**
  368. 368 : * Symbole "Tâches", est utilisé pour savoir si il faut mettre à jours uniquement les tâches ou non.
  369. 369 : */
  370. 370 : tasks: Symbol('tasks'),
  371. 371 : },
  372. 372 : nextcloud: {
  373. 373 : folder: Symbol('folder'),
  374. 374 : file: Symbol('file'),
  375. 375 : },
  376. 376 : navigator: {
  377. 377 : firefox: Symbol('firefox'),
  378. 378 : },
  379. 379 : null: Symbol('null'),
  380. 380 : },
  381. 381 : /**
  382. 382 : * Les différents Identifiants
  383. 383 : */
  384. 384 : Ids: {
  385. 385 : /**
  386. 386 : * Ids pour le menu.
  387. 387 : */
  388. 388 : menu: {
  389. 389 : /**
  390. 390 : * Id des différents badges du menu.
  391. 391 : */
  392. 392 : badge: {
  393. 393 : calendar: 'menu-badge-calendar',
  394. 394 : tasks: 'menu-badge-tasks',
  395. 395 : mail: 'menu-badge-mail',
  396. 396 : ariane: 'menu-badge-ariane',
  397. 397 : },
  398. 398 : },
  399. 399 : create: {
  400. 400 : doc_input: 'generated-document-input-mel-metapage',
  401. 401 : doc_input_ext: 'generated-document-input-mel-metapage-ext',
  402. 402 : doc_input_hidden: 'generated-document-input-mel-metapage-hidden',
  403. 403 : doc_input_path: 'generated-document-select-mel-metapage-path',
  404. 404 : },
  405. 405 : },
  406. 406 : PopUp: {
  407. 407 : /**
  408. 408 : * Ouvre la popup de chat
  409. 409 : * @returns
  410. 410 : */
  411. 411 : open_ariane() {
  412. 412 : if (rcmail.busy) return;
  413. 413 :
  414. 414 : if (mel_metapage.PopUp.ariane === null) {
  415. 415 : mel_metapage.PopUp.ariane = new ArianePopUp(ArianeButton.default());
  416. 416 : rcmail.addEventListener('toggle-options-user', (show) => {
  417. 417 : let $iframe =
  418. 418 : mel_metapage.PopUp.ariane.ariane.card.body.card.find('iframe');
  419. 419 :
  420. 420 : if (show.show === true) $iframe.css('z-index', '1');
  421. 421 : else $iframe.css('z-index', '');
  422. 422 : });
  423. 423 : }
  424. 424 :
  425. 425 : if (mel_metapage.PopUp.ariane.is_show === true)
  426. 426 : mel_metapage.PopUp.ariane.hide();
  427. 427 : else mel_metapage.PopUp.ariane.show();
  428. 428 : },
  429. 429 : ariane: null,
  430. 430 : },
  431. 431 : Other: {
  432. 432 : webconf: {
  433. 433 : private: '/group',
  434. 434 : },
  435. 435 : },
  436. 436 : RCMAIL_Start: {
  437. 437 : async ping_nextcloud() {
  438. 438 : if (
  439. 439 : rcmail.env.nextcloud_url !== undefined &&
  440. 440 : rcmail.env.nextcloud_url !== null &&
  441. 441 : rcmail.env.nextcloud_url !== ''
  442. 442 : ) {
  443. 443 : rcmail.env.nextcloud_pinged = await ping(rcmail.env.nextcloud_url);
  444. 444 : if (rcmail.env.nextcloud_pinged === false)
  445. 445 : rcmail.env.nextcloud_pinged = await ping(
  446. 446 : rcmail.env.nextcloud_url,
  447. 447 : true,
  448. 448 : );
  449. 449 : }
  450. 450 : },
  451. 451 : },
  452. 452 : Frames: {
  453. 453 : max: 10,
  454. 454 : lastFrames: [],
  455. 455 : add(frame) {
  456. 456 : if (parent !== window) return parent.mel_metapage.Frames.add(frame);
  457. 457 :
  458. 458 : if (frame?.task === 'webconf') return this;
  459. 459 :
  460. 460 : if (this.lastFrames.length + 1 > 5) this.lastFrames.pop();
  461. 461 :
  462. 462 : this.lastFrames.push(frame);
  463. 463 : return this;
  464. 464 : },
  465. 465 : reset() {
  466. 466 : if (parent !== window) return parent.mel_metapage.Frames.reset();
  467. 467 :
  468. 468 : this.lastFrames = [];
  469. 469 : return this;
  470. 470 : },
  471. 471 : pop() {
  472. 472 : if (parent !== window) return parent.mel_metapage.Frames.pop();
  473. 473 :
  474. 474 : if (this.lastFrames.length === 0) return null;
  475. 475 :
  476. 476 : return this.lastFrames.pop();
  477. 477 : },
  478. 478 : last(it = 0) {
  479. 479 : if (parent !== window) return parent.mel_metapage.Frames.last(it);
  480. 480 :
  481. 481 : if (this.lastFrames.length === 0) return null;
  482. 482 :
  483. 483 : return this.lastFrames[this.lastFrames.length - 1 - it];
  484. 484 : },
  485. 485 : back(_default = 'home') {
  486. 486 : if (parent !== window) return parent.mel_metapage.Frames.back(_default);
  487. 487 :
  488. 488 : const unexist = mel_metapage.Storage.unexist;
  489. 489 : let last = this.pop()?.task || _default;
  490. 490 :
  491. 491 : if (last === unexist) last = _default;
  492. 492 :
  493. 493 : return mel_metapage.Functions.change_frame(last, true, true).then(() => {
  494. 494 : if ($('.menu-last-frame').hasClass('disabled')) {
  495. 495 : m_mp_ChangeLasteFrameInfo();
  496. 496 : }
  497. 497 : });
  498. 498 : },
  499. 499 : create_frame(name, task, icon) {
  500. 500 : return {
  501. 501 : name,
  502. 502 : task,
  503. 503 : icon,
  504. 504 : };
  505. 505 : },
  506. 506 : },
  507. 507 : Functions: {
  508. 508 : /**
  509. 509 : * Copie un texte dans le press-papier
  510. 510 : * @param {string} text Texte à copier
  511. 511 : */
  512. 512 : copy(text) {
  513. 513 : function copyOnClick(val) {
  514. 514 : var tempInput = document.createElement('input');
  515. 515 : tempInput.value = val;
  516. 516 : document.body.appendChild(tempInput);
  517. 517 : tempInput.select();
  518. 518 : document.execCommand('copy');
  519. 519 : document.body.removeChild(tempInput);
  520. 520 : }
  521. 521 :
  522. 522 : copyOnClick(text);
  523. 523 : rcmail.display_message(
  524. 524 : `${text} copier dans le presse-papier.`,
  525. 525 : 'confirmation',
  526. 526 : );
  527. 527 :
  528. 528 : return this;
  529. 529 : },
  530. 530 :
  531. 531 : /**
  532. 532 : * Récupère les données du calendrier entre deux dates.
  533. 533 : * @param {moment} start (moment) Début des évènements à récupérer
  534. 534 : * @param {moment} end (moment) Fin des évènements à récupérer
  535. 535 : */
  536. 536 : update_calendar(start, end) {
  537. 537 : start = start.format('YYYY-MM-DDTHH:mm:ss');
  538. 538 : end = end.format('YYYY-MM-DDTHH:mm:ss');
  539. 539 :
  540. 540 : if (rcmail.env.ev_calendar_url === undefined)
  541. 541 : rcmail.env.ev_calendar_url = ev_calendar_url;
  542. 542 :
  543. 543 : return $.ajax({
  544. 544 : // fonction permettant de faire de l'ajax
  545. 545 : type: 'GET', // methode de transmission des données au fichier php
  546. 546 : url:
  547. 547 : rcmail.env.ev_calendar_url +
  548. 548 : `&source=${mceToRcId(rcmail.env.username)}` +
  549. 549 : '&start=' +
  550. 550 : start +
  551. 551 : '&end=' +
  552. 552 : end, // url du fichier php
  553. 553 : success: function (data) {
  554. 554 : try {
  555. 555 : let events = [];
  556. 556 : data = JSON.parse(data);
  557. 557 : data = Enumerable.from(data)
  558. 558 : .where(
  559. 559 : (x) =>
  560. 560 : mel_metapage.Functions.check_if_date_is_okay(
  561. 561 : x.start,
  562. 562 : x.end,
  563. 563 : start,
  564. 564 : ) ||
  565. 565 : mel_metapage.Functions.check_if_date_is_okay(
  566. 566 : x.start,
  567. 567 : x.end,
  568. 568 : end,
  569. 569 : ),
  570. 570 : )
  571. 571 : .toArray();
  572. 572 : return data;
  573. 573 : } catch (ex) {
  574. 574 : console.error(ex);
  575. 575 : rcmail.display_message(
  576. 576 : 'Une erreur est survenue lors de la synchronisation.',
  577. 577 : 'error',
  578. 578 : );
  579. 579 : }
  580. 580 : },
  581. 581 : error: function (xhr, ajaxOptions, thrownError) {
  582. 582 : // Add these parameters to display the required response
  583. 583 : console.error(xhr, ajaxOptions, thrownError);
  584. 584 : rcmail.display_message(
  585. 585 : 'Une erreur est survenue lors de la synchronisation.',
  586. 586 : 'error',
  587. 587 : );
  588. 588 : },
  589. 589 : });
  590. 590 : },
  591. 591 :
  592. 592 : /**
  593. 593 : * Vérifie si un élement est valide ou si c'est un doublon ou une instance originale
  594. 594 : * @param {JSON} element
  595. 595 : * @param {Array<JSON>} events
  596. 596 : * @param {boolean} test
  597. 597 : * @returns {boolean|JSON} True si pas de problème | Evènement problématique
  598. 598 : */
  599. 599 : check_if_calendar_valid(element, events, test = true) {
  600. 600 : if (mceToRcId(rcmail.env.username) !== element.calendar) return false;
  601. 601 : else {
  602. 602 : if (element._instance !== undefined && test) {
  603. 603 : for (let it = 0; it < events.length; it++) {
  604. 604 : const event = events[it];
  605. 605 : if (event.uid === element.uid && event._instance === undefined)
  606. 606 : return event;
  607. 607 : }
  608. 608 : }
  609. 609 : }
  610. 610 : return true;
  611. 611 : },
  612. 612 :
  613. 613 : /**
  614. 614 : * Vérifie si une date se trouve entre 2 dates
  615. 615 : * @param {string|moment} sd
  616. 616 : * @param {string|moment} ed
  617. 617 : * @param {string|moment} date
  618. 618 : * @returns {boolean}
  619. 619 : */
  620. 620 : check_if_date_is_okay(sd, ed, date) {
  621. 621 : if (typeof sd === 'string') sd = moment(sd).startOf('day');
  622. 622 :
  623. 623 : if (typeof ed === 'string') ed = moment(ed).endOf('day');
  624. 624 :
  625. 625 : if (typeof date === 'string') date = moment(date);
  626. 626 :
  627. 627 : startDate = moment(date).startOf('day');
  628. 628 : endDate = moment(date).endOf('day');
  629. 629 :
  630. 630 : if (startDate <= sd && sd <= endDate) return true;
  631. 631 : else if (startDate <= ed && ed <= endDate) return true;
  632. 632 : else if (
  633. 633 : sd <= startDate &&
  634. 634 : startDate <= ed &&
  635. 635 : sd <= endDate &&
  636. 636 : endDate <= ed
  637. 637 : )
  638. 638 : return true;
  639. 639 : else return false;
  640. 640 : },
  641. 641 :
  642. 642 : get_from_url(url) {
  643. 643 : const URL_VARIABLE = '/?';
  644. 644 : const URL_SEPARATOR = '&';
  645. 645 : url = url.split(URL_VARIABLE)[1].split(URL_SEPARATOR);
  646. 646 : let datas = {};
  647. 647 :
  648. 648 : for (let index = 0, len = url.length; index < len; ++index) {
  649. 649 : const element = url[index].split('=');
  650. 650 : datas[element[0]] = element[1];
  651. 651 : }
  652. 652 :
  653. 653 : return datas;
  654. 654 : },
  655. 655 :
  656. 656 : /**
  657. 657 : * Récupère une URL conforme.
  658. 658 : * @param {string} task Tâche
  659. 659 : * @param {string} action Action
  660. 660 : * @param {JSON} args divers arguments ex {_eventType:1}
  661. 661 : * @returns {string}
  662. 662 : */
  663. 663 : url(task, action = '', args = null) {
  664. 664 : let url = task;
  665. 665 : if (action !== null && action !== undefined && action !== '')
  666. 666 : url += '&_action=' + action;
  667. 667 :
  668. 668 : if (
  669. 669 : window.location.href.includes(
  670. 670 : `${rcmail.env.mel_metapage_const.key}=${rcmail.env.mel_metapage_const.value}`,
  671. 671 : ) ||
  672. 672 : window !== parent
  673. 673 : ) {
  674. 674 : if (args === null || args === undefined) {
  675. 675 : args = {};
  676. 676 : args[rcmail.env.mel_metapage_const.key] =
  677. 677 : rcmail.env.mel_metapage_const.value;
  678. 678 : } else if (args[rcmail.env.mel_metapage_const.key] === undefined)
  679. 679 : args[rcmail.env.mel_metapage_const.key] =
  680. 680 : rcmail.env.mel_metapage_const.value;
  681. 681 : }
  682. 682 :
  683. 683 : if (args !== null) {
  684. 684 : for (const key in args) {
  685. 685 : if (Object.hasOwnProperty.call(args, key)) {
  686. 686 : const element = args[key];
  687. 687 : url += '&' + key + '=' + element;
  688. 688 : }
  689. 689 : }
  690. 690 : }
  691. 691 : return rcmail.get_task_url(
  692. 692 : url,
  693. 693 : window.location.origin + window.location.pathname,
  694. 694 : );
  695. 695 : },
  696. 696 :
  697. 697 : public_url(path, args = null) {
  698. 698 : let url =
  699. 699 : window.location.origin +
  700. 700 : window.location.pathname +
  701. 701 : (window.location.pathname[window.location.pathname.length - 1] === '/'
  702. 702 : ? ''
  703. 703 : : '/') +
  704. 704 : 'public/' +
  705. 705 : path;
  706. 706 :
  707. 707 : if (args !== null) {
  708. 708 : for (const key in args) {
  709. 709 : if (Object.hasOwnProperty.call(args, key)) {
  710. 710 : const element = args[key];
  711. 711 : url += (url.includes('?') ? '&' : '?') + key + '=' + element;
  712. 712 : }
  713. 713 : }
  714. 714 : }
  715. 715 :
  716. 716 : return url;
  717. 717 : },
  718. 718 :
  719. 719 : /**
  720. 720 : * Change de frame, même si l'on est pas depuis "TOP"
  721. 721 : * @param {string} frame Frame à ouvrir
  722. 722 : * @param {boolean} changepage Si vrai, on change de page, sinon la page ouverte sera caché.
  723. 723 : * @param {boolean} waiting Si l'on veux attendre que la frame sois ouverte ou non.
  724. 724 : * @param {JSON} args Arguments à ajouter dans l'url de la frame.
  725. 725 : */
  726. 726 : async change_frame(
  727. 727 : frame,
  728. 728 : changepage = true,
  729. 729 : waiting = false,
  730. 730 : args = null,
  731. 731 : actions = [],
  732. 732 : ) {
  733. 733 : await PageManager.SwitchFrame(frame, { changepage, args, actions });
  734. 734 : // var busy;
  735. 735 : // if (changepage) busy = (top ?? window).rcmail.set_busy(true, 'loading');
  736. 736 :
  737. 737 : // // if (frame === "webconf")
  738. 738 : // // {
  739. 739 : // // var initial_change_page = changepage;
  740. 740 : // // changepage = false;
  741. 741 : // // }
  742. 742 :
  743. 743 : // // if (waiting)
  744. 744 : // // mel_metapage.Storage.set(
  745. 745 : // // mel_metapage.Storage.wait_frame_loading,
  746. 746 : // // mel_metapage.Storage.wait_frame_waiting,
  747. 747 : // // );
  748. 748 :
  749. 749 : // (top ?? window).rcmail.env.can_change_while_busy = true;
  750. 750 : // let promise = (top ?? window).mm_st_OpenOrCreateFrame(
  751. 751 : // frame,
  752. 752 : // changepage,
  753. 753 : // args,
  754. 754 : // actions,
  755. 755 : // );
  756. 756 :
  757. 757 : // if (waiting) await promise;
  758. 758 : // // if (waiting) {
  759. 759 : // // await wait(
  760. 760 : // // () =>
  761. 761 : // // mel_metapage.Storage.get(
  762. 762 : // // mel_metapage.Storage.wait_frame_loading,
  763. 763 : // // ) !== mel_metapage.Storage.wait_frame_loaded,
  764. 764 : // // );
  765. 765 : // // mel_metapage.Storage.remove(mel_metapage.Storage.wait_frame_loading);
  766. 766 : // // }
  767. 767 :
  768. 768 : // // if (frame === "webconf")
  769. 769 : // // {
  770. 770 : // // if (initial_change_page)
  771. 771 : // // {
  772. 772 : // // (top ?? window).mm_st_OpenOrCreateFrame(frame, initial_change_page, args, actions);
  773. 773 : // // }
  774. 774 : // // //this.update_refresh_thing();
  775. 775 : // // }
  776. 776 :
  777. 777 : // if (changepage && busy) {
  778. 778 : // (top ?? window).rcmail.set_busy(false, 'loading', busy);
  779. 779 : // busy = null;
  780. 780 : // }
  781. 781 :
  782. 782 : return this;
  783. 783 : },
  784. 784 :
  785. 785 : /**
  786. 786 : * Change de page en utilisant le combo tâche + action.
  787. 787 : * @param {string} task
  788. 788 : * @param {string} action
  789. 789 : * @param {JSON} params
  790. 790 : * @returns
  791. 791 : */
  792. 792 : async change_page(
  793. 793 : task,
  794. 794 : action = null,
  795. 795 : params = {},
  796. 796 : update = true,
  797. 797 : force = false,
  798. 798 : ) {
  799. 799 : let contracted_task;
  800. 800 : let $querry;
  801. 801 :
  802. 802 : if (window !== parent)
  803. 803 : return await parent.mel_metapage.Functions.change_page(
  804. 804 : task,
  805. 805 : action,
  806. 806 : params,
  807. 807 : update,
  808. 808 : force,
  809. 809 : );
  810. 810 :
  811. 811 : contracted_task = mm_st_ClassContract(task);
  812. 812 :
  813. 813 : if (action !== null) params['_action'] = action;
  814. 814 :
  815. 815 : $querry = $(`iframe.${task}-frame`);
  816. 816 :
  817. 817 : if (update) {
  818. 818 : if ($querry.length > 0) {
  819. 819 : params[rcmail.env.mel_metapage_const.key] =
  820. 820 : rcmail.env.mel_metapage_const.value;
  821. 821 : action = mel_metapage.Functions.url(task, null, params);
  822. 822 :
  823. 823 : try {
  824. 824 : if ($querry[0].contentWindow.location.href !== action)
  825. 825 : $querry[0].src = action;
  826. 826 : else if (force) {
  827. 827 : $querry[0].contentWindow.location.reload();
  828. 828 : }
  829. 829 : } catch (error) {}
  830. 830 : } else if ($(`.${task}-frame`).length > 0) {
  831. 831 : action = mel_metapage.Functions.url(task, null, params);
  832. 832 : if (window.location.href !== action) $(`.${task}-frame`).remove();
  833. 833 : }
  834. 834 : }
  835. 835 :
  836. 836 : return await this.change_frame(contracted_task, true, true, params);
  837. 837 : },
  838. 838 :
  839. 839 : /**
  840. 840 : * Ouvre la page de chat, avec un can/group/tem associé ou non.
  841. 841 : * @param {string} channel_or_group group/ ou channel/
  842. 842 : * @returns {Promise}
  843. 843 : */
  844. 844 : open_chat(channel_or_group = null) {
  845. 845 : return mel_metapage.Functions.change_frame('rocket', true, true).then(
  846. 846 : () => {
  847. 847 : if (channel_or_group !== null && channel_or_group !== undefined) {
  848. 848 : if (channel_or_group[0] !== '/')
  849. 849 : channel_or_group = `/${channel_or_group}`;
  850. 850 : parent.$('.discussion-frame')[0].contentWindow.postMessage(
  851. 851 : {
  852. 852 : externalCommand: 'go',
  853. 853 : path: channel_or_group,
  854. 854 : },
  855. 855 : '*',
  856. 856 : );
  857. 857 : }
  858. 858 : },
  859. 859 : );
  860. 860 : },
  861. 861 :
  862. 862 : /**
  863. 863 : * F5 une frame
  864. 864 : * @param {string} frame Classe de la frame à refresh
  865. 865 : * @returns Chaînage
  866. 866 : */
  867. 867 : update_frame(frame) {
  868. 868 : this.call('reload_frame', false, {
  869. 869 : _integrated: true,
  870. 870 : always: true,
  871. 871 : args: [`${frame}-frame`],
  872. 872 : });
  873. 873 :
  874. 874 : return this;
  875. 875 : },
  876. 876 :
  877. 877 : /**
  878. 878 : * Reviens à la frame d'avant.
  879. 879 : * @param {boolean} wait Si l'on doit attendre le changement de frame ou non.
  880. 880 : * @param {string} default_frame Frame par défaut si il n'y a pas de frame d'avant. Si null, ne fait rien dans ce cas.
  881. 881 : */
  882. 882 : async frame_back(wait = true, default_frame = null) {
  883. 883 : let last = await this.ask('rcmail.env.last_frame_class');
  884. 884 :
  885. 885 : if (
  886. 886 : last === null ||
  887. 887 : last === undefined ||
  888. 888 : last === mel_metapage.Storage.unexist
  889. 889 : ) {
  890. 890 : if (default_frame !== null) last = default_frame;
  891. 891 : else return;
  892. 892 : }
  893. 893 :
  894. 894 : const tmp = await this.change_frame(last, true, wait);
  895. 895 :
  896. 896 : top.rcmail.clear_messages();
  897. 897 :
  898. 898 : for (const iterator of $('iframe.mm-frame')) {
  899. 899 : iterator.contentWindow.rcmail.clear_messages();
  900. 900 : }
  901. 901 :
  902. 902 : return tmp;
  903. 903 : },
  904. 904 :
  905. 905 : get_current_title(current_task = null, _default = document.title) {
  906. 906 : if (parent !== window)
  907. 907 : return parent.mel_metapage.Function.get_current_title(
  908. 908 : current_task,
  909. 909 : _default,
  910. 910 : );
  911. 911 :
  912. 912 : if (current_task === null)
  913. 913 : current_task = (top ?? window).rcmail.env.current_task;
  914. 914 :
  915. 915 : if (current_task === 'chat' || current_task === 'discussion') {
  916. 916 : return 'Discussion';
  917. 917 : } else if (current_task === 'webconf') {
  918. 918 : return 'Visioconférence';
  919. 919 : } else {
  920. 920 : const frame = $(`iframe.${current_task}-frame`);
  921. 921 : if (frame.length > 0) return frame[0].contentDocument.title || _default;
  922. 922 : else if ($(`.${current_task}-frame`).length > 0) return document.title;
  923. 923 : else return _default;
  924. 924 : }
  925. 925 : },
  926. 926 :
  927. 927 : /**
  928. 928 : * Execute un string depuis "TOP"
  929. 929 : * @param {string} exec String à éxécuter
  930. 930 : * @param {string} child Exécuter aussi dans les fenetres filles ?
  931. 931 : * @param {JSON} args Autres arguments (eval etc....)
  932. 932 : */
  933. 933 : call(exec, child = false, args = {}) {
  934. 934 : if (typeof exec !== 'string') {
  935. 935 : const tmp_exec = JSON.stringify(exec);
  936. 936 : if (tmp_exec === undefined) {
  937. 937 : if (typeof exec === 'function') exec = `(${exec.toString()})()`;
  938. 938 : else exec = exec.toString();
  939. 939 : } else exec = tmp_exec;
  940. 940 : }
  941. 941 :
  942. 942 : let config = {
  943. 943 : exec: exec,
  944. 944 : child: child,
  945. 945 : };
  946. 946 :
  947. 947 : if (args != null && args !== undefined) {
  948. 948 : for (const key in args) {
  949. 949 : if (Object.hasOwnProperty.call(args, key)) {
  950. 950 : const element = args[key];
  951. 951 : config[key] = element;
  952. 952 : }
  953. 953 : }
  954. 954 : }
  955. 955 :
  956. 956 : if (
  957. 957 : window.workspaces !== undefined &&
  958. 958 : window.workspaces.sync !== undefined
  959. 959 : )
  960. 960 : workspaces.sync.PostToParent(config);
  961. 961 : else {
  962. 962 : parent.postMessage(config);
  963. 963 : }
  964. 964 :
  965. 965 : return this;
  966. 966 : },
  967. 967 :
  968. 968 : /**
  969. 969 : * Execute du script à un contexte au dessus.
  970. 970 : * @param {string} exec String à éxécuter
  971. 971 : * @param {boolean} child Exécuter aussi dans les fenetres filles ?
  972. 972 : * @param {JSON} args Autres arguments (eval etc....)
  973. 973 : */
  974. 974 : async callAsync(exec, child = false, args = {}) {
  975. 975 : mel_metapage.Storage.set(
  976. 976 : mel_metapage.Storage.wait_call_loading,
  977. 977 : mel_metapage.Storage.wait_frame_waiting,
  978. 978 : );
  979. 979 : this.call(exec, child, args);
  980. 980 : await wait(() => {
  981. 981 : return (
  982. 982 : mel_metapage.Storage.get(mel_metapage.Storage.wait_call_loading) ===
  983. 983 : mel_metapage.Storage.wait_frame_waiting
  984. 984 : );
  985. 985 : });
  986. 986 :
  987. 987 : return this;
  988. 988 : },
  989. 989 :
  990. 990 : /**
  991. 991 : * Modifie l'url du navigateur
  992. 992 : * @param {string} url URL à afficher
  993. 993 : */
  994. 994 : title(url) {
  995. 995 : mel_metapage.Functions.call(
  996. 996 : `window.history.replaceState({}, document.title, '${url}')`,
  997. 997 : );
  998. 998 : return this;
  999. 999 : },
  1000. 1000 :
  1001. 1001 : /**
  1002. 1002 : * Met (ou pas) la frame en cours et parente en loading.
  1003. 1003 : * @param {boolean} busy
  1004. 1004 : */
  1005. 1005 : busy(busy = true) {
  1006. 1006 : const framed = window !== parent;
  1007. 1007 : mel_metapage.Storage.set('mel.busy', busy);
  1008. 1008 : if (busy) {
  1009. 1009 : this.call("rcmail.set_busy(true, 'loading')");
  1010. 1010 : if (framed) rcmail.set_busy(true, 'loading');
  1011. 1011 : } else {
  1012. 1012 : this.call('rcmail.set_busy(false);rcmail.clear_messages();');
  1013. 1013 : if (framed) {
  1014. 1014 : rcmail.set_busy(false);
  1015. 1015 : rcmail.clear_messages();
  1016. 1016 : }
  1017. 1017 : }
  1018. 1018 :
  1019. 1019 : return this;
  1020. 1020 : },
  1021. 1021 :
  1022. 1022 : /**
  1023. 1023 : * Vérifie si l'application est occupée.
  1024. 1024 : * @returns {boolean}
  1025. 1025 : */
  1026. 1026 : is_busy() {
  1027. 1027 : const framed = window !== parent && rcmail.busy != undefined;
  1028. 1028 : if (framed)
  1029. 1029 : return mel_metapage.Storage.get('mel.busy') === true || rcmail.busy;
  1030. 1030 : else {
  1031. 1031 : if (rcmail.busy === undefined)
  1032. 1032 : return mel_metapage.Storage.get('mel.busy') === true;
  1033. 1033 : else
  1034. 1034 : return rcmail.busy || mel_metapage.Storage.get('mel.busy') === true;
  1035. 1035 : }
  1036. 1036 : },
  1037. 1037 :
  1038. 1038 : /**
  1039. 1039 : * @async
  1040. 1040 : * Récupère une variable globale parente.
  1041. 1041 : * @param {string} props Variable à récupérer
  1042. 1042 : * @returns {Promise<any>} Valeur de la variable
  1043. 1043 : */
  1044. 1044 : async ask(props) {
  1045. 1045 : this.call(`mel_metapage.Storage.set("mel.ask", ${props})`);
  1046. 1046 : await wait(() => mel_metapage.Storage.get('mel.ask') === null);
  1047. 1047 : props = mel_metapage.Storage.get('mel.ask');
  1048. 1048 : mel_metapage.Storage.remove('mel.ask');
  1049. 1049 : return props;
  1050. 1050 : },
  1051. 1051 :
  1052. 1052 : updateRichText(html) {
  1053. 1053 : return html.replace(/</g, '&lt;');
  1054. 1054 : },
  1055. 1055 :
  1056. 1056 : remove_accents(string) {
  1057. 1057 : return string.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  1058. 1058 : },
  1059. 1059 :
  1060. 1060 : replace_dets(string = '', rep = '') {
  1061. 1061 : const dets = [
  1062. 1062 : ' le ',
  1063. 1063 : ' la ',
  1064. 1064 : ' les ',
  1065. 1065 : ' un ',
  1066. 1066 : ' une ',
  1067. 1067 : ' de ',
  1068. 1068 : ' des ',
  1069. 1069 : ' mon ',
  1070. 1070 : ' ma ',
  1071. 1071 : ' tes ',
  1072. 1072 : ' ton ',
  1073. 1073 : ' ta ',
  1074. 1074 : ' son ',
  1075. 1075 : ' sa ',
  1076. 1076 : ' ses ',
  1077. 1077 : ' notre ',
  1078. 1078 : ' nos ',
  1079. 1079 : ' vos ',
  1080. 1080 : ' votre ',
  1081. 1081 : ' leur ',
  1082. 1082 : ' leurs ',
  1083. 1083 : ' se ',
  1084. 1084 : ' ce ',
  1085. 1085 : ' cette ',
  1086. 1086 : ' cet ',
  1087. 1087 : ' ces ',
  1088. 1088 : ' a ',
  1089. 1089 : "l'",
  1090. 1090 : ];
  1091. 1091 :
  1092. 1092 : for (const iterator of dets) {
  1093. 1093 : string = string.replaceAll(iterator, rep);
  1094. 1094 : }
  1095. 1095 :
  1096. 1096 : while (string[0] === rep) {
  1097. 1097 : string = string.slice(1, string.length);
  1098. 1098 : }
  1099. 1099 :
  1100. 1100 : return string;
  1101. 1101 : },
  1102. 1102 :
  1103. 1103 : replace_special_char(string, rep = '') {
  1104. 1104 : const regexp = /[^a-zA-Z0-9_ -]/g;
  1105. 1105 : string = string.replace(regexp, rep);
  1106. 1106 :
  1107. 1107 : while (string[0] === rep) {
  1108. 1108 : string = string.slice(1, string.length);
  1109. 1109 : }
  1110. 1110 :
  1111. 1111 : return string;
  1112. 1112 : },
  1113. 1113 :
  1114. 1114 : /**
  1115. 1115 : *
  1116. 1116 : * @param {string} url
  1117. 1117 : * @returns
  1118. 1118 : */
  1119. 1119 : webconf_url(url) {
  1120. 1120 : if (url[url.length - 1] === '&') url = url.slice(0, url.length - 1);
  1121. 1121 :
  1122. 1122 : let val = url.toUpperCase();
  1123. 1123 :
  1124. 1124 : if (val.includes(rcmail.env['webconf.base_url'].toUpperCase())) {
  1125. 1125 : val = val.split('/');
  1126. 1126 : val = val[val.length - 1];
  1127. 1127 : val = val.toUpperCase();
  1128. 1128 : } else if (val.includes('PUBLIC/WEBCONF'))
  1129. 1129 : val = val.split('_KEY=')[1].split('&')[0];
  1130. 1130 : else {
  1131. 1131 : let link = WebconfLink.create({ location: url, categories: [] });
  1132. 1132 : val = link.key;
  1133. 1133 :
  1134. 1134 : try {
  1135. 1135 : if (val === '') {
  1136. 1136 : link = WebconfLink.create({
  1137. 1137 : location: `#visio:${url}`,
  1138. 1138 : categories: [],
  1139. 1139 : });
  1140. 1140 : val = link.key;
  1141. 1141 : }
  1142. 1142 : } catch (error) {
  1143. 1143 : val = null;
  1144. 1144 : }
  1145. 1145 : }
  1146. 1146 :
  1147. 1147 : return val || null;
  1148. 1148 : },
  1149. 1149 :
  1150. 1150 : _shuffle(array) {
  1151. 1151 : var currentIndex = array.length,
  1152. 1152 : temporaryValue,
  1153. 1153 : randomIndex;
  1154. 1154 : // While there remain elements to shuffle...
  1155. 1155 : while (currentIndex !== 0) {
  1156. 1156 : // Pick a remaining element...
  1157. 1157 : randomIndex = Math.floor(Math.random() * currentIndex);
  1158. 1158 : currentIndex -= 1;
  1159. 1159 :
  1160. 1160 : // And swap it with the current element.
  1161. 1161 : temporaryValue = array[currentIndex];
  1162. 1162 : array[currentIndex] = array[randomIndex];
  1163. 1163 : array[randomIndex] = temporaryValue;
  1164. 1164 : }
  1165. 1165 : return array;
  1166. 1166 : },
  1167. 1167 :
  1168. 1168 : generateWebconfRoomName() {
  1169. 1169 : var charArray = [
  1170. 1170 : 'A',
  1171. 1171 : 'B',
  1172. 1172 : 'C',
  1173. 1173 : 'D',
  1174. 1174 : 'E',
  1175. 1175 : 'F',
  1176. 1176 : 'G',
  1177. 1177 : 'H',
  1178. 1178 : 'I',
  1179. 1179 : 'J',
  1180. 1180 : 'K',
  1181. 1181 : 'L',
  1182. 1182 : 'M',
  1183. 1183 : 'N',
  1184. 1184 : 'O',
  1185. 1185 : 'P',
  1186. 1186 : 'Q',
  1187. 1187 : 'R',
  1188. 1188 : 'S',
  1189. 1189 : 'T',
  1190. 1190 : 'U',
  1191. 1191 : 'V',
  1192. 1192 : 'W',
  1193. 1193 : 'X',
  1194. 1194 : 'Y',
  1195. 1195 : 'Z',
  1196. 1196 : ];
  1197. 1197 : var digitArray = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
  1198. 1198 : var roomName =
  1199. 1199 : this._shuffle(digitArray).join('').substring(0, 3) +
  1200. 1200 : this._shuffle(charArray).join('').substring(0, 7);
  1201. 1201 : return this._shuffle(roomName.split('')).join('');
  1202. 1202 : },
  1203. 1203 :
  1204. 1204 : /**
  1205. 1205 : * Faire facilement une requête ajax
  1206. 1206 : * @param {string} url
  1207. 1207 : * @param {JSON} datas
  1208. 1208 : * @param {function} success
  1209. 1209 : * @param {function} failed
  1210. 1210 : * @param {string} type
  1211. 1211 : * @returns {Promise<any>} Appel ajax
  1212. 1212 : */
  1213. 1213 : ajax(
  1214. 1214 : url,
  1215. 1215 : datas = mel_metapage.Symbols.null,
  1216. 1216 : success = (datas) => {},
  1217. 1217 : failed = (xhr, ajaxOptions, thrownError) => {
  1218. 1218 : console.error(xhr, ajaxOptions, thrownError);
  1219. 1219 : },
  1220. 1220 : type = 'POST',
  1221. 1221 : ) {
  1222. 1222 : let config = {
  1223. 1223 : // fonction permettant de faire de l'ajax
  1224. 1224 : type: type, // methode de transmission des données au fichier php
  1225. 1225 : url: url, //rcmail.env.ev_calendar_url+'&start='+dateNow(new Date())+'&end='+dateNow(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()+1)), // url du fichier php
  1226. 1226 : success: success,
  1227. 1227 : error: failed,
  1228. 1228 : };
  1229. 1229 : if (datas !== mel_metapage.Symbols.null) config['data'] = datas;
  1230. 1230 :
  1231. 1231 : return $.ajax(config);
  1232. 1232 : },
  1233. 1233 :
  1234. 1234 : /**
  1235. 1235 : * Execute un appel ajax get
  1236. 1236 : * @param {string} url
  1237. 1237 : * @param {JSON} datas
  1238. 1238 : * @param {function} success
  1239. 1239 : * @param {function} failed
  1240. 1240 : * @returns {Promise<any>}
  1241. 1241 : */
  1242. 1242 : get(
  1243. 1243 : url,
  1244. 1244 : datas = {},
  1245. 1245 : success = (datas) => {},
  1246. 1246 : failed = (xhr, ajaxOptions, thrownError) => {
  1247. 1247 : console.error(xhr, ajaxOptions, thrownError);
  1248. 1248 : },
  1249. 1249 : ) {
  1250. 1250 : for (const key in datas) {
  1251. 1251 : if (Object.hasOwnProperty.call(datas, key)) {
  1252. 1252 : const element = datas[key];
  1253. 1253 : url += `${url.includes('?') ? '&' : '?'}${key}=${encodeURIComponent(element)}`;
  1254. 1254 : }
  1255. 1255 : }
  1256. 1256 : return this.ajax(url, mel_metapage.Symbols.null, success, failed, 'GET');
  1257. 1257 : },
  1258. 1258 :
  1259. 1259 : /**
  1260. 1260 : * Execute un appel ajax post
  1261. 1261 : * @param {string} url
  1262. 1262 : * @param {Symbol|JSON} datas <c>mel_metapage.Symbols.null</c> si aucune données.
  1263. 1263 : * @param {function} success
  1264. 1264 : * @param {function} failed
  1265. 1265 : */
  1266. 1266 : post(
  1267. 1267 : url,
  1268. 1268 : datas = mel_metapage.Symbols.null,
  1269. 1269 : success = (datas) => {},
  1270. 1270 : failed = (xhr, ajaxOptions, thrownError) => {
  1271. 1271 : console.error(xhr, ajaxOptions, thrownError);
  1272. 1272 : },
  1273. 1273 : ) {
  1274. 1274 : return this.ajax(url, datas, success, failed);
  1275. 1275 : },
  1276. 1276 :
  1277. 1277 : /**
  1278. 1278 : * Contient différents fonctions pour mettre à jours certaines données.
  1279. 1279 : */
  1280. 1280 : update: {
  1281. 1281 : /**
  1282. 1282 : * Met à jours le calendrier.
  1283. 1283 : */
  1284. 1284 : calendar() {
  1285. 1285 : mel_metapage.Functions.call(
  1286. 1286 : 'rcmail.mel_metapage_fn.calendar_updated();',
  1287. 1287 : );
  1288. 1288 : return this;
  1289. 1289 : },
  1290. 1290 : },
  1291. 1291 :
  1292. 1292 : /**
  1293. 1293 : * Vérifie si un handler existe sur un élément.
  1294. 1294 : * @param {DOMElement} element Element à tester.
  1295. 1295 : * @param {function} handler Fonction à vérifier
  1296. 1296 : * @param {string} type Type d'évènement
  1297. 1297 : * @returns {boolean}
  1298. 1298 : */
  1299. 1299 : handlerExist(element, handler, type = 'click') {
  1300. 1300 : if (element.val !== undefined) element = element[0];
  1301. 1301 : return Enumerable.from(jQuery._data(element, 'events')[type])
  1302. 1302 : .where((x) => x.handler + '' === handler + '')
  1303. 1303 : .any();
  1304. 1304 : },
  1305. 1305 :
  1306. 1306 : /**
  1307. 1307 : * Fonctions lié au stockage nextcloud.
  1308. 1308 : */
  1309. 1309 : stockage: {
  1310. 1310 : /**
  1311. 1311 : * Ouvre la frame nextcloud et affiche un document en particulier (si il existe).
  1312. 1312 : * @param {JSON|Nextcloud_File} datas Données du document à afficher.
  1313. 1313 : * {
  1314. 1314 : file:nom du fichier,
  1315. 1315 : folder:chemin du fichier
  1316. 1316 : }
  1317. 1317 : * @param {boolean} isfiledatas Si vrai, datas est un objet <c>Nextcloud_File</c>
  1318. 1318 : * @param {function} thenFunc Action à faire une fois que l'on à changer de page.
  1319. 1319 : */
  1320. 1320 : go(datas, goFunc = null, thenFunc = null) {
  1321. 1321 : let init = 'new Nextcloud("rcmail.env.nextcloud_username")';
  1322. 1322 : let go = `.go(${JSON.stringify(datas)}, ${goFunc})`;
  1323. 1323 : let then = '';
  1324. 1324 :
  1325. 1325 : if (thenFunc !== null) {
  1326. 1326 : then = `.then((a) => { (${thenFunc + ''})(a) })`;
  1327. 1327 : }
  1328. 1328 :
  1329. 1329 : return mel_metapage.Functions.call(init + go + then);
  1330. 1330 : },
  1331. 1331 : have_0_quota() {
  1332. 1332 : return rcmail.env.have_0_quota ?? false;
  1333. 1333 : },
  1334. 1334 : is_stockage_active() {
  1335. 1335 : const DEFAULT = false;
  1336. 1336 : return rcmail?.env?.why_is_not_active?.value
  1337. 1337 : ? rcmail.env.why_is_not_active.value ===
  1338. 1338 : rcmail.env.why_is_not_active.consts.ST_ACTIVE
  1339. 1339 : : DEFAULT;
  1340. 1340 : },
  1341. 1341 : canDriveActions() {
  1342. 1342 : return !this.have_0_quota() && this.is_stockage_active();
  1343. 1343 : },
  1344. 1344 : },
  1345. 1345 :
  1346. 1346 : /**
  1347. 1347 : * Recherche dans les mails
  1348. 1348 : * @param {string} itemToSearch Objet à chercher
  1349. 1349 : * @param {Array<string>} fields Champs
  1350. 1350 : * @param {boolean} openFrame Ouvrir ou non la frame
  1351. 1351 : */
  1352. 1352 : async searchOnMail(itemToSearch, fields, openFrame = false) {
  1353. 1353 : if (parent === window) {
  1354. 1354 : if (openFrame)
  1355. 1355 : await mel_metapage.Functions.change_frame('mail', true, true);
  1356. 1356 :
  1357. 1357 : if ($('iframe.mail-frame').length > 0)
  1358. 1358 : $('iframe.mail-frame')[0].contentWindow.postMessage({
  1359. 1359 : exec: 'search',
  1360. 1360 : _integrated: true,
  1361. 1361 : child: false,
  1362. 1362 : args: [itemToSearch, fields],
  1363. 1363 : });
  1364. 1364 : else search_action(itemToSearch, fields);
  1365. 1365 : } else
  1366. 1366 : mel_metapage.Functions.call(
  1367. 1367 : `mel_metapage.Functions.searchOnMail('${itemToSearch}', ${JSON.stringify(fields)}, ${openFrame})`,
  1368. 1368 : );
  1369. 1369 :
  1370. 1370 : return this;
  1371. 1371 : },
  1372. 1372 :
  1373. 1373 : doActionFrame(frame, doAction, ...functionArgs) {
  1374. 1374 : //console.log("[doActionFrame]",frame, doAction, parent !== window);
  1375. 1375 : if (parent !== window) {
  1376. 1376 : mel_metapage.Functions.call('mel_metapage.doActionFrame', false, {
  1377. 1377 : _integrated: true,
  1378. 1378 : always: true,
  1379. 1379 : args: [frame, doAction + '', ...functionArgs],
  1380. 1380 : });
  1381. 1381 : } else {
  1382. 1382 : if (typeof doAction === 'string') {
  1383. 1383 : doAction = new Function(`return (${doAction})(...arguments)`);
  1384. 1384 : }
  1385. 1385 :
  1386. 1386 : //Personne ouvert
  1387. 1387 : if ($(`.${frame}-frame`).length === 0) doAction(0, ...functionArgs);
  1388. 1388 : else if ($(`iframe.${frame}-frame`).length > 0)
  1389. 1389 : //Frame ouverte
  1390. 1390 : doAction(1, ...functionArgs);
  1391. 1391 : //Page ouverte
  1392. 1392 : else doAction(2, ...functionArgs);
  1393. 1393 : }
  1394. 1394 :
  1395. 1395 : return this;
  1396. 1396 : },
  1397. 1397 :
  1398. 1398 : update_refresh_thing() {
  1399. 1399 : let current = (top ?? window).$('.refresh-current-thing');
  1400. 1400 : let action =
  1401. 1401 : window.webconf_helper.already() ||
  1402. 1402 : (top ?? window).rcmail.env.current_frame_name === 'webconf';
  1403. 1403 :
  1404. 1404 : if (action === true)
  1405. 1405 : current.addClass('disabled').attr('disabled', 'disabled');
  1406. 1406 : else current.removeClass('disabled').removeAttr('disabled');
  1407. 1407 :
  1408. 1408 : return this;
  1409. 1409 : },
  1410. 1410 :
  1411. 1411 : isNavigator(symbol) {
  1412. 1412 : switch (symbol) {
  1413. 1413 : case mel_metapage.Symbols.navigator.firefox:
  1414. 1414 : return typeof InstallTrigger !== 'undefined';
  1415. 1415 :
  1416. 1416 : default:
  1417. 1417 : throw 'Unknown navigator';
  1418. 1418 : }
  1419. 1419 : },
  1420. 1420 :
  1421. 1421 : /**
  1422. 1422 : *
  1423. 1423 : * @param {string|JSON} _params - String sous forme '?dir=PATH&fileid=ID' ou JSON sous forme {path:'', id:''}
  1424. 1424 : */
  1425. 1425 : async change_frame_nextcloud(_params = null) {
  1426. 1426 : const appfiles = '/apps/files/';
  1427. 1427 : const urlToAdd =
  1428. 1428 : _params.id === undefined && _params.includes(appfiles) ? appfiles : '';
  1429. 1429 : let param = urlToAdd + (_params ?? '');
  1430. 1430 : let configArgs = null;
  1431. 1431 :
  1432. 1432 : //si _params n'est pas un string
  1433. 1433 : if (_params !== null) {
  1434. 1434 : if (_params.id !== undefined)
  1435. 1435 : param = `${urlToAdd}?dir=${_params.path}&fileid=${_params.id}`;
  1436. 1436 :
  1437. 1437 : configArgs = {
  1438. 1438 : _params: urlencode(param),
  1439. 1439 : };
  1440. 1440 : }
  1441. 1441 :
  1442. 1442 : const url = `${rcmail.env.nextcloud_url}${param ?? ''}`;
  1443. 1443 :
  1444. 1444 : if (parent.$('iframe.stockage-frame').length > 0)
  1445. 1445 : parent
  1446. 1446 : .$('iframe.stockage-frame')[0]
  1447. 1447 : .contentWindow.$('#mel_nextcloud_frame')[0].src = url;
  1448. 1448 :
  1449. 1449 : await this.change_frame('stockage', true, true, configArgs);
  1450. 1450 :
  1451. 1451 : parent.$(
  1452. 1452 : 'iframe.stockage-frame',
  1453. 1453 : )[0].contentWindow.rcmail.env.nextcloud_gotourl = url;
  1454. 1454 :
  1455. 1455 : return this;
  1456. 1456 : },
  1457. 1457 :
  1458. 1458 : async comment_mail(uid, comment, folder = 'INBOX') {
  1459. 1459 : let data = uid;
  1460. 1460 : await this.post(
  1461. 1461 : mel_metapage.Functions.url('mel_metapage', 'comment_mail'),
  1462. 1462 : {
  1463. 1463 : _uid: uid,
  1464. 1464 : _comment: comment,
  1465. 1465 : _folder: folder,
  1466. 1466 : },
  1467. 1467 : (datas) => {
  1468. 1468 : if (datas == 'false') {
  1469. 1469 : rcmail.display_message('Une erreur est survenue!', 'error');
  1470. 1470 : data = false;
  1471. 1471 : } else data = datas;
  1472. 1472 : },
  1473. 1473 : );
  1474. 1474 :
  1475. 1475 : return data;
  1476. 1476 : },
  1477. 1477 :
  1478. 1478 : calculateObjectSizeInMo(obj) {
  1479. 1479 : const jsonString = JSON.stringify(obj);
  1480. 1480 : const blob = new Blob([jsonString]);
  1481. 1481 : const sizeInBytes = blob.size;
  1482. 1482 : const sizeInMb = sizeInBytes / (1024 * 1024);
  1483. 1483 : return sizeInMb;
  1484. 1484 : },
  1485. 1485 :
  1486. 1486 : colors: {
  1487. 1487 : kMel_Luminance(rgb) {
  1488. 1488 : let R = rgb[0] / 255;
  1489. 1489 : let G = rgb[1] / 255;
  1490. 1490 : let B = rgb[2] / 255;
  1491. 1491 :
  1492. 1492 : if (R <= 0.04045) {
  1493. 1493 : R = R / 12.92;
  1494. 1494 : } else {
  1495. 1495 : R = ((R + 0.055) / 1.055) ** 2.4;
  1496. 1496 : }
  1497. 1497 : if (G <= 0.04045) {
  1498. 1498 : G = G / 12.92;
  1499. 1499 : } else {
  1500. 1500 : G = ((G + 0.055) / 1.055) ** 2.4;
  1501. 1501 : }
  1502. 1502 : if (B <= 0.04045) {
  1503. 1503 : B = B / 12.92;
  1504. 1504 : } else {
  1505. 1505 : B = ((B + 0.055) / 1.055) ** 2.4;
  1506. 1506 : }
  1507. 1507 :
  1508. 1508 : const L = 0.2126 * R + 0.7152 * G + 0.0722 * B;
  1509. 1509 :
  1510. 1510 : return L;
  1511. 1511 : },
  1512. 1512 :
  1513. 1513 : kMel_CompareLuminance(rgb1, rgb2) {
  1514. 1514 : const l1 = this.kMel_Luminance(rgb1);
  1515. 1515 : const l2 = this.kMel_Luminance(rgb2);
  1516. 1516 :
  1517. 1517 : let ratio;
  1518. 1518 : if (l1 > l2) {
  1519. 1519 : ratio = (l1 + 0.05) / (l2 + 0.05);
  1520. 1520 : } else {
  1521. 1521 : ratio = (l2 + 0.05) / (l1 + 0.05);
  1522. 1522 : }
  1523. 1523 :
  1524. 1524 : return ratio;
  1525. 1525 : },
  1526. 1526 :
  1527. 1527 : kMel_LuminanceRatioAAA(rgb1, rgb2) {
  1528. 1528 : const isAAA = this.kMel_CompareLuminance(rgb1, rgb2) > 4.5;
  1529. 1529 : return isAAA;
  1530. 1530 : },
  1531. 1531 :
  1532. 1532 : kMel_extractRGB(color) {
  1533. 1533 : let regexp = /#[a-fA-F\d]{6}/g;
  1534. 1534 : let rgbArray = color.match(regexp);
  1535. 1535 :
  1536. 1536 : if (rgbArray) {
  1537. 1537 : rgbArray[0] = parseInt(color.slice(1, 3), 16);
  1538. 1538 : rgbArray[1] = parseInt(color.slice(3, 5), 16);
  1539. 1539 : rgbArray[2] = parseInt(color.slice(5, 7), 16);
  1540. 1540 :
  1541. 1541 : return rgbArray;
  1542. 1542 : }
  1543. 1543 :
  1544. 1544 : regexp = /#[a-fA-F\d]{3}/g;
  1545. 1545 : rgbArray = color.match(regexp);
  1546. 1546 :
  1547. 1547 : if (rgbArray) {
  1548. 1548 : rgbArray[0] = parseInt(color.slice(1, 2), 16);
  1549. 1549 : rgbArray[1] = parseInt(color.slice(2, 3), 16);
  1550. 1550 : rgbArray[2] = parseInt(color.slice(3, 4), 16);
  1551. 1551 :
  1552. 1552 : return rgbArray;
  1553. 1553 : }
  1554. 1554 :
  1555. 1555 : regexp = /\d+/g;
  1556. 1556 : rgbArray = color.match(regexp);
  1557. 1557 :
  1558. 1558 : if (rgbArray.length === 3 || rgbArray.length === 4) {
  1559. 1559 : return rgbArray;
  1560. 1560 : }
  1561. 1561 : },
  1562. 1562 : },
  1563. 1563 : },
  1564. 1564 : };
  1565. 1565 :
  1566. 1566 : window.mel_metapage = mel_metapage;