Subversion Repositories oidplus

Rev

Rev 122 | View as "text/javascript" | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * OIDplus 2.0
  3.  * Copyright 2019 Daniel Marschall, ViaThinkSoft
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17.  
  18. /*jshint esversion: 6 */
  19.  
  20. // $('#html').jstree();
  21.  
  22. current_node = "";
  23. popstate_running = false;
  24.  
  25. String.prototype.explode = function (separator, limit) {
  26.         // https://stackoverflow.com/questions/4514323/javascript-equivalent-to-php-explode
  27.         const array = this.split(separator);
  28.         if (limit !== undefined && array.length >= limit) {
  29.                 array.push(array.splice(limit - 1).join(separator));
  30.         }
  31.         return array;
  32. };
  33.  
  34. String.prototype.htmlentities = function () {
  35.         return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
  36. };
  37.  
  38. String.prototype.html_entity_decode = function () {
  39.         return $('<textarea />').html(this).text();
  40. };
  41.  
  42. function getMeta(metaName) {
  43.         const metas = document.getElementsByTagName('meta');
  44.  
  45.         for (let i = 0; i < metas.length; i++) {
  46.                 if (metas[i].getAttribute('name') === metaName) {
  47.                         return metas[i].getAttribute('content');
  48.                 }
  49.         }
  50.  
  51.         return '';
  52. }
  53.  
  54. function getOidPlusSystemTitle() {
  55.         return getMeta('OIDplus-SystemTitle')
  56. }
  57.  
  58. function combine_systemtitle_and_pagetitle(systemtitle, pagetitle) {
  59.         if (systemtitle == pagetitle) {
  60.                 return systemtitle;
  61.         } else {
  62.                 return systemtitle + ' - ' + pagetitle;
  63.         }
  64. }
  65.  
  66. function getTreeLoadURL() {
  67.         var url = new URL(window.location.href);
  68.         var goto = url.searchParams.get("goto");
  69.         return (goto != null) ? "ajax.php?action=tree_load&goto="+encodeURIComponent(goto)
  70.                               : "ajax.php?action=tree_load";
  71. }
  72.  
  73. function reloadContent() {
  74.         // document.location = "?goto="+encodeURIComponent(current_node);
  75.         openOidInPanel(current_node, false);
  76.         $('#oidtree').jstree("refresh");
  77. }
  78.  
  79. function x_rec(x_data, i) {
  80.         $('#oidtree').jstree('open_node', x_data[i], function(e, data) {
  81.                 if (i+1 < x_data.length) {
  82.                         x_rec(x_data, i+1);
  83.                 } else {
  84.                         popstate_running = true; // don't call openOidInPanel again
  85.                         try {
  86.                                 $('#oidtree').jstree('select_node', x_data[i]);
  87.                         } catch (err) {
  88.                                 popstate_running = false;
  89.                         } finally {
  90.                                 popstate_running = false;
  91.                         }
  92.                 }
  93.         });
  94. }
  95.  
  96. function openOidInPanel(id, reselect=false) {
  97.         if (reselect) {
  98.                 $('#oidtree').jstree('deselect_all');
  99.  
  100.                 popstate_running = true; // don't call openOidInPanel during tree selection
  101.                 try {
  102.                         // If the node is already loaded in the tree, select it
  103.                         if (!$('#oidtree').jstree('select_node', id)) {
  104.                                 // If the node is not loaded, then we try to search it.
  105.                                 // If it can be found, then open all parent nodes and select the node
  106.                                 $.ajax({
  107.                                         url:"ajax.php",
  108.                                         method:"POST",
  109.                                         data:{
  110.                                                 action:"tree_search",
  111.                                                 search:id
  112.                                         },
  113.                                         error:function(jqXHR, textStatus, errorThrown) {
  114.                                                 console.error("Error: " + errorThrown);
  115.                                         },
  116.                                         success:function(data) {
  117.                                                 if ("error" in data) {
  118.                                                         console.error(data);
  119.                                                 } else if ((data instanceof Array) && (data.length > 0)) {
  120.                                                         x_rec(data, 0);
  121.                                                 } else {
  122.                                                         console.error(data);
  123.                                                 }
  124.                                         }
  125.                                 });
  126.                         }
  127.                 } catch (err) {
  128.                         popstate_running = false;
  129.                 } finally {
  130.                         popstate_running = false;
  131.                 }
  132.         }
  133.  
  134.         // This loads the actual content
  135.  
  136.         document.title = "";
  137.         $('#real_title').html("&nbsp;");
  138.         $('#real_content').html("Loading...");
  139.         $('#static_link').attr("href", "index.php?goto="+encodeURIComponent(id));
  140.  
  141.         // Normal opening of a description
  142.         fetch('ajax.php?action=get_description&id='+encodeURIComponent(id))
  143.         .then(function(response) {
  144.                 response.json()
  145.                 .then(function(data) {
  146.                         if ("error" in data) {
  147.                                 alert("Failed to load content: " + data.error);
  148.                                 console.error(data.error);
  149.                                 return;
  150.                         }
  151.  
  152.                         data.id = id;
  153.  
  154.                         document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.title);
  155.                         var state = {
  156.                                 "node_id":id,
  157.                                 "titleHTML":(data.icon ? '<img src="'+data.icon+'" width="48" height="48" alt="'+data.title.htmlentities()+'"> ' : '') + data.title.htmlentities(),
  158.                                 "textHTML":data.text,
  159.                                 "staticlinkHREF":"index.php?goto="+encodeURIComponent(id),
  160.                         };
  161.                         if (current_node != id) {
  162.                                 window.history.pushState(state, data.title, "?goto="+encodeURIComponent(id));
  163.                         } else {
  164.                                 window.history.replaceState(state, data.title, "?goto="+encodeURIComponent(id));
  165.                         }
  166.  
  167.                         if (data.icon) {
  168.                                 $('#real_title').html('<img src="'+data.icon+'" width="48" height="48" alt="'+data.title.htmlentities()+'"> ' + data.title.htmlentities());
  169.                         } else {
  170.                                 $('#real_title').html(data.title.htmlentities());
  171.                         }
  172.                         $('#real_content').html(data.text);
  173.                         document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.title);
  174.                         current_node = id;
  175.                 })
  176.                 .catch(function(error) {
  177.                         alert("Failed to load content: " + error);
  178.                         console.error(error);
  179.                 });
  180.         })
  181.         .catch(function(error) {
  182.                 alert("Failed to load content: " + error);
  183.                 console.error(error);
  184.         });
  185. }
  186.  
  187. function updateDesc() {
  188.         $.ajax({
  189.                 url:"ajax.php",
  190.                 method:"POST",
  191.                 data: {
  192.                         action:"Update2",
  193.                         id:current_node,
  194.                         title:(document.getElementById('titleedit') ? document.getElementById('titleedit').value : null),
  195.                         //description:(document.getElementById('description') ? document.getElementById('description').value : null)
  196.                         description:tinyMCE.get('description').getContent()
  197.                 },
  198.                 error:function(jqXHR, textStatus, errorThrown) {
  199.                         alert("Error: " + errorThrown);
  200.                 },
  201.                 success:function(data) {
  202.                         if ("error" in data) {
  203.                                 alert("Error: " + data.error);
  204.                         } else if (data.status == 0) {
  205.                                 alert("Update OK");
  206.                                 //reloadContent();
  207.                                 $('#oidtree').jstree("refresh");
  208.                                 var h1s = document.getElementsByTagName("h1");
  209.                                 for (var i = 0; i < h1s.length; i++) {
  210.                                         var h1 = h1s[i];
  211.                                         h1.innerHTML = document.getElementById('titleedit').value.htmlentities();
  212.                                 }
  213.                                 document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), document.getElementById('titleedit').value);
  214.  
  215.                                 var mce = tinymce.get('description');
  216.                                 if (mce != null) mce.isNotDirty = 1;
  217.                         } else {
  218.                                 alert("Error: " + data.error);
  219.                         }
  220.                 }
  221.         });
  222. }
  223.  
  224. function crudActionSendInvitation(origin, email) {
  225.         // document.location = "?goto=oidplus:invite_ra$"+encodeURIComponent(email)+"$"+encodeURIComponent(origin);
  226.         openOidInPanel('oidplus:invite_ra$'+email+'$'+origin, false);
  227. }
  228.  
  229. function crudActionInsert(parent) {
  230.         $.ajax({
  231.                 url:"ajax.php",
  232.                 method:"POST",
  233.                 data:{
  234.                         action:"Insert",
  235.                         id:document.getElementById('id').value,
  236.                         ra_email:document.getElementById('ra_email').value,
  237.                         asn1ids:(document.getElementById('asn1ids') ? document.getElementById('asn1ids').value : null),
  238.                         iris:(document.getElementById('iris') ? document.getElementById('iris').value : null),
  239.                         confidential:(document.getElementById('hide') ? document.getElementById('hide').checked : null),
  240.                         parent:parent
  241.                 },
  242.                 error:function(jqXHR, textStatus, errorThrown) {
  243.                         alert("Error: " + errorThrown);
  244.                 },
  245.                 success:function(data) {
  246.                         if ("error" in data) {
  247.                                 alert("Error: " + data.error);
  248.                         } else if (data.status == 0) {
  249.                                 //alert("Insert OK");
  250.                                 reloadContent();
  251.                                 // TODO: auf reloadContent() verzichten. stattdessen nur tree links aktualisieren, und rechts eine neue zeile zur tabelle hinzufügen
  252.                         } else if (data.status == 1) {
  253.                                 if (confirm("Update OK. However, the email address you have entered ("+document.getElementById('ra_email').value+") is not in our system. Do you want to send an invitation, so that the RA can register an account to manage their OIDs?")) {
  254.                                         crudActionSendInvitation(parent, document.getElementById('ra_email').value);
  255.                                 } else {
  256.                                         reloadContent();
  257.                                         // TODO: auf reloadContent() verzichten. stattdessen nur tree links aktualisieren, und rechts eine neue zeile zur tabelle hinzufügen
  258.                                 }
  259.                         } else {
  260.                                 alert("Error: " + data);
  261.                         }
  262.                 }
  263.         });
  264. }
  265.  
  266. function crudActionUpdate(id, parent) {
  267.         $.ajax({
  268.                 url:"ajax.php",
  269.                 method:"POST",
  270.                 data: {
  271.                         action:"Update",
  272.                         id:id,
  273.                         ra_email:document.getElementById('ra_email_'+id).value,
  274.                         asn1ids:(document.getElementById('asn1ids_'+id) ? document.getElementById('asn1ids_'+id).value : null),
  275.                         iris:(document.getElementById('iris_'+id) ? document.getElementById('iris_'+id).value : null),
  276.                         confidential:(document.getElementById('hide_'+id) ? document.getElementById('hide_'+id).checked : null),
  277.                         parent:parent
  278.                 },
  279.                 error:function(jqXHR, textStatus, errorThrown) {
  280.                         alert("Error: " + errorThrown);
  281.                 },
  282.                 success:function(data) {
  283.                         if ("error" in data) {
  284.                                 alert("Error: " + data.error);
  285.                         } else if (data.status == 0) {
  286.                                 alert("Update OK");
  287.                                 // reloadContent();
  288.                                 $('#oidtree').jstree("refresh");
  289.                         } else if (data.status == 1) {
  290.                                 if (confirm("Update OK. However, the email address you have entered ("+document.getElementById('ra_email_'+id).value+") is not in our system. Do you want to send an invitation, so that the RA can register an account to manage their OIDs?")) {
  291.                                         crudActionSendInvitation(parent, document.getElementById('ra_email_'+id).value);
  292.                                 } else {
  293.                                         // reloadContent();
  294.                                         $('#oidtree').jstree("refresh");
  295.                                 }
  296.                         } else {
  297.                                 alert("Error: " + data);
  298.                         }
  299.                 }
  300.         });
  301. }
  302.  
  303. function crudActionDelete(id, parent) {
  304.         if(!window.confirm("Are you sure that you want to delete "+id+"?")) return false;
  305.  
  306.         $.ajax({
  307.                 url:"ajax.php",
  308.                 method:"POST",
  309.                 data: {
  310.                         action:"Delete",
  311.                         id:id,
  312.                         parent:parent
  313.                 },
  314.                 error:function(jqXHR, textStatus, errorThrown) {
  315.                         alert("Error: " + errorThrown);
  316.                 },
  317.                 success:function(data) {
  318.                         if ("error" in data) {
  319.                                 alert("Error: " + data.error);
  320.                         } else if (data.status == 0) {
  321.                                 reloadContent();
  322.                                 // TODO: auf reloadContent() verzichten. stattdessen nur tree links aktualisieren, und rechts die zeile aus der tabelle löschen
  323.                         } else {
  324.                                 alert("Error: " + data.error);
  325.                         }
  326.                 }
  327.         });
  328. }
  329.  
  330. function deleteRa(email, goto) {
  331.         if(!window.confirm("Are you really sure that you want to delete "+email+"? (The OIDs stay active)")) return false;
  332.  
  333.         $.ajax({
  334.                 url:"ajax.php",
  335.                 method:"POST",
  336.                 data: {
  337.                         action:"delete_ra",
  338.                         email:email,
  339.                 },
  340.                 error:function(jqXHR, textStatus, errorThrown) {
  341.                         alert("Error: " + errorThrown);
  342.                 },
  343.                 success:function(data) {
  344.                         if ("error" in data) {
  345.                                 alert("Error: " + data.error);
  346.                         } else if (data.status == 0) {
  347.                                 alert("Done.");
  348.                                 if (goto != null) document.location = "?goto="+encodeURIComponent(goto);
  349.                                 // reloadContent();
  350.                         } else {
  351.                                 alert("Error: " + data.error);
  352.                         }
  353.                 }
  354.         });
  355. }
  356.  
  357. // This function opens the "parentID" node, and then selects the "childID" node (which should be beneath the parent node)
  358. function openAndSelectNode(childID, parentID) {
  359.         if ($('#oidtree').jstree(true).get_node(parentID)) {
  360.                 $('#oidtree').jstree('open_node', parentID, function(e, data) { // open parent node
  361.                         if ($('#oidtree').jstree(true).get_node(childID)) { // is the child there?
  362.                                 $('#oidtree').jstree('deselect_all').jstree('select_node', childID); // select it
  363.                         } else {
  364.                                 // This can happen if the content page contains brand new items which are not in the treeview yet
  365.                                 document.location = "?goto="+encodeURIComponent(childID);
  366.                         }
  367.                 }, true);
  368.         } else {
  369.                 // This should usually not happen
  370.                 document.location = "?goto="+encodeURIComponent(childID);
  371.         }
  372. }
  373.  
  374. $(window).on("popstate", function(e) {
  375.         popstate_running = true;
  376.         try {
  377.                 var data = e.originalEvent.state;
  378.  
  379.                 current_node = data.node_id;
  380.                 $('#oidtree').jstree('deselect_all').jstree('select_node', data.node_id);
  381.                 $('#real_title').html(data.titleHTML);
  382.                 $('#real_content').html(data.textHTML);
  383.                 $('#static_link').attr("href", data.staticlinkHREF);
  384.                 document.title = combine_systemtitle_and_pagetitle(getOidPlusSystemTitle(), data.titleHTML.html_entity_decode());
  385.         } catch (err) {
  386.                 popstate_running = false;
  387.         } finally {
  388.                 popstate_running = false;
  389.         }
  390. });
  391.  
  392. $(document).ready(function () {
  393.         // --- JsTree
  394.  
  395.         $('#oidtree')
  396.         .jstree({
  397.                 plugins: ['massload','search','conditionalselect'],
  398.                 'core' : {
  399.                         'data' : {
  400.                                 "url" : getTreeLoadURL(),
  401.                                 "data" : function (node) {
  402.                                         return { "id" : node.id };
  403.                                 }
  404.                         },
  405.                         "multiple": false
  406.                 },
  407.                 'conditionalselect' : function (node) {
  408.                         if (node.original.conditionalselect !== undefined) {
  409.                                 return eval(node.original.conditionalselect);
  410.                         } else {
  411.                                 return true; // allow select
  412.                         }
  413.                 },
  414.         })
  415.         .on('ready.jstree', function (e, data) {
  416.                 var url = new URL(window.location.href);
  417.                 var goto = url.searchParams.get("goto");
  418.                 if (goto == null) goto = "oidplus:system"; // the page was not called with ?goto=...
  419.  
  420.                 // By setting current_node, select_node() will not cause ajax.php?action=get_description to load (since we already loaded the first static content via PHP, for search engines mainly)
  421.                 // But then we need to set the history state manually
  422.                 current_node = goto;
  423.                 window.history.replaceState({
  424.                         "node_id":goto,
  425.                         "titleHTML":$('#real_title').html(),
  426.                         "textHTML":$('#real_content').html(),
  427.                         "staticlinkHREF":"index.php?goto="+encodeURIComponent(goto),
  428.                 }, $('#real_title').html(), "?goto="+encodeURIComponent(goto));
  429.  
  430.                 if (goto != null) data.instance.select_node([goto]);
  431.         })
  432.         .on('select_node.jstree', function (node, selected, event) {
  433.                 mobileNavClose();
  434.  
  435.                 var id = selected.node.id;
  436.                 if ((!popstate_running) && (current_node != id)) {
  437.                         openOidInPanel(id, false);
  438.                 }
  439.         });
  440.  
  441.         // --- Layout
  442.  
  443.         document.getElementById('system_title_menu').style.display = "block";
  444.  
  445.         $('#oidtree').addClass('ui-layout-west');
  446.         $('#content_window').addClass('ui-layout-center');
  447.         $('#system_title_bar').addClass('ui-layout-north');
  448.         var layout = $('#frames').layout({
  449.                 north__size:                  40,
  450.                 north__slidable:              false,
  451.                 north__closable:              false,
  452.                 north__resizable:             false,
  453.                 west__size:                   450,
  454.                 west__spacing_closed:         20,
  455.                 west__togglerLength_closed:   230,
  456.                 west__togglerAlign_closed:    "top",
  457.                 west__togglerContent_closed:  "O<br>B<br>J<br>E<br>C<br>T<br><br>T<BR>R<BR>E<BR>E",
  458.                 west__togglerTip_closed:      "Open & Pin Menu",
  459.                 west__sliderTip:              "Slide Open Menu",
  460.                 west__slideTrigger_open:      "mouseover",
  461.                 center__maskContents:         true // IMPORTANT - enable iframe masking
  462.         });
  463.         layout.sizePane("west", 451); // We need this weird hack because of a Chrome bug that appears when you reload the page a few times
  464. });
  465.  
  466. function mobileNavClose() {
  467.         if ($("#system_title_menu").is(":hidden")) {
  468.                 return;
  469.         }
  470.  
  471.         $("#oidtree").slideUp("medium").promise().done(function() {
  472.                 $("#oidtree").addClass("ui-layout-west");
  473.                 $("#oidtree").show();
  474.         });
  475.         $("#system_title_menu").removeClass("active");
  476. }
  477.  
  478. function mobileNavOpen() {
  479.         $("#oidtree").hide();
  480.         $("#oidtree").removeClass("ui-layout-west");
  481.         $("#oidtree").slideDown("medium");
  482.         $("#system_title_menu").addClass("active");
  483. }
  484.  
  485. function mobileNavButtonClick(sender) {
  486.         if ($("#oidtree").hasClass("ui-layout-west")) {
  487.                 mobileNavOpen();
  488.         } else {
  489.                 mobileNavClose();
  490.         }
  491. }
  492.  
  493. function mobileNavButtonHover(sender) {
  494.         sender.classList.toggle("hover");
  495. }
  496.