notifs.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <?php
  2. require '../../lib/glob.php';
  3. require '../../lib/muoribene.php';
  4. require '../../lib/sessionstart.php';
  5. require '../../lib/myconn.php';
  6. require '../../lib/getadmacc.php';
  7. if ($account['Level'] == 'guest')
  8. muoribene('Sorry, you are not authorized.', true);
  9. require '../../lib/menu.php';
  10. $menu['menu']['selected'] = true;
  11. $menu['menu']['submenu']['notifs']['selected'] = true;
  12. buildmenu($menu);
  13. $dbg = '';
  14. // praticamente una macro
  15. function hspech($str)
  16. {
  17. return (htmlspecialchars($str, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
  18. }
  19. function are_valid_ids($ids)
  20. {
  21. if (count($ids) <= 0) return false;
  22. foreach ($ids as $id) {
  23. if (!(preg_match('/^[0-9]+$/', $id) === 1)) {
  24. return false;
  25. }
  26. }
  27. return true;
  28. }
  29. if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER["CONTENT_TYPE"] === "application/json") {
  30. $response = array(
  31. "done" => true,
  32. "error" => ""
  33. );
  34. $body = json_decode(file_get_contents('php://input'), true);
  35. if (array_key_exists('act', $body) &&
  36. array_key_exists('ids', $body) &&
  37. are_valid_ids($body["ids"])) {
  38. switch ($body['act']) {
  39. case "massread":
  40. mysqli_query($link, 'UPDATE Notifications SET Seen=1 WHERE ID in (' . implode(", ", $body["ids"]) . ')')
  41. or muoribene(mysqli_error($link), true);
  42. break;
  43. case "massdelete":
  44. mysqli_query($link, 'UPDATE Notifications SET Deleted=1 WHERE ID in (' . implode(", ", $body["ids"]) . ')')
  45. or muoribene(mysqli_error($link), true);
  46. break;
  47. default:
  48. http_response_code(400);
  49. $response["done"] = false;
  50. $response["error"] = "Unknown act.";
  51. }
  52. } else {
  53. http_response_code(400);
  54. $response["done"] = false;
  55. $response["error"] = "Bad request.";
  56. }
  57. echo(json_encode($response));
  58. mysqli_close($link);
  59. exit(0);
  60. }
  61. $dbg .= $dlang . '<br>' . N;
  62. $dbg .= '<pre>' . print_r($_GET, 1) . '</pre>';
  63. require '../../lib/notifs.php';
  64. $notifs = notifs($link);
  65. $filtordon = false;
  66. if ($filtordon) {
  67. $filtordimgoff = 'imgs/cerca_act_off.svg';
  68. $filtordimgon = 'imgs/cerca_act_on.svg';
  69. } else {
  70. $filtordimgoff = 'imgs/cerca_off.svg';
  71. $filtordimgon = 'imgs/cerca_on.svg';
  72. }
  73. ?>
  74. <!DOCTYPE HTML>
  75. <html lang="en">
  76. <head>
  77. <title>Mustard - Notifs</title>
  78. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  79. <meta name="description" content="Admin pages for Mastodon Help">
  80. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  81. <link rel="icon" type="image/png" href="imgs/icona-32.png" sizes="32x32">
  82. <link rel="icon" type="image/png" href="imgs/icona-192.png" sizes="192x192">
  83. <link rel="icon" type="image/png" href="imgs/icona-512.png" sizes="512x512">
  84. <link rel="apple-touch-icon-precomposed" href="imgs/icona-180.png">
  85. <script language="JavaScript" src="js/menu.js?v=<?php echo($cjrand); ?>"></script>
  86. <script language="JavaScript" src="js/confirma.js?v=<?php echo($cjrand); ?>"></script>
  87. <link rel="stylesheet" type="text/css" href="css/theme.css?v=<?php echo($cjrand); ?>">
  88. <link rel="stylesheet" type="text/css" href="css/notifs.css?v=<?php echo($cjrand); ?>">
  89. <script language="JavaScript">
  90. <!--
  91. <?php require 'js/notifs.js.php'; ?>
  92. function reverseSelection() {
  93. let checkboxes = getAliveCheckboxes()
  94. checkboxes.forEach(function (box) {
  95. box.checked = !box.checked;
  96. })
  97. let n_selected = getSelectedNotifs().length;
  98. if (n_selected > 0) {
  99. setBtnsVisibility("visible");
  100. } else if (n_selected === 0) {
  101. setBtnsVisibility("hidden");
  102. document.getElementById("global-selector").checked = false;
  103. }
  104. if (n_selected === checkboxes.length) {
  105. document.getElementById("global-selector").checked = true;
  106. }
  107. }
  108. function setBtnsVisibility(state) {
  109. let controls = document.querySelector("#notifs-list-controls");
  110. controls.querySelector("#read-btn").style.visibility = state;
  111. controls.querySelector("#delete-btn").style.visibility = state;
  112. }
  113. function toggleState(check) {
  114. if (check.checked) {
  115. setBtnsVisibility("visible");
  116. } else {
  117. setBtnsVisibility("hidden")
  118. }
  119. let checkboxes = getAliveCheckboxes()
  120. checkboxes.forEach(function (box) {
  121. box.checked = check.checked;
  122. });
  123. }
  124. function notifSelect(check) {
  125. let checkboxes = getAliveCheckboxes()
  126. if (check.checked) {
  127. setBtnsVisibility("visible");
  128. } else if (checkboxes.every(box => !box.checked)) {
  129. setBtnsVisibility("hidden");
  130. }
  131. }
  132. function showFilterPanel() {
  133. let plancia = document.querySelector("#plancia");
  134. if (plancia.style.display !== "block") {
  135. if (plancia.childElementCount <= 3) {
  136. addAfter(plancia.firstElementChild);
  137. }
  138. plancia.style.display = "block";
  139. }
  140. }
  141. function getFilters() {
  142. return Array.from(document.querySelectorAll("#plancia .notifs-criterion"));
  143. }
  144. function newFilter() {
  145. return "<div class='notifs-criterion'>" +
  146. " <label for='start-date'>Da</label>" +
  147. " <input id='start-date' name='start-date' type='date'>" +
  148. " <label for='stop-date'>A</label>" +
  149. " <input id='stop-date' name='stop-date' type='date'>" +
  150. " <select id='severity'>" +
  151. " <option value='-1'>Indifferente</option>" +
  152. " <option value='1'>Normal</option>" +
  153. " <option value='2'>Warning</option>" +
  154. " <option value='3'>Error</option>" +
  155. " </select>" +
  156. " <select id='operation'>" +
  157. " <option value='0'>E</option>" +
  158. " <option value='1'>O</option>" +
  159. " </select>" +
  160. " <img class='plus' src='imgs/plus.svg' title='Aggiungi un criterio sotto questo'" +
  161. " onclick='addAfter(this.parentElement)'>" +
  162. " <img class='minus' src='imgs/minus.svg' title='Rimuovi questo criterio'" +
  163. " onclick='removeFilter(this.parentElement)'>" +
  164. "</div>";
  165. }
  166. function addAfter(elem) {
  167. let filters = getFilters();
  168. if (filters.length < 7) {
  169. elem.insertAdjacentHTML('afterend', newFilter());
  170. filters = getFilters();
  171. if (filters.length > 1) {
  172. let select = elem.querySelector("#operation");
  173. if (select.disabled) {
  174. select.disabled = false;
  175. }
  176. }
  177. filters[filters.length - 1].querySelector("#operation").disabled = true;
  178. }
  179. }
  180. function removeFilter(elem) {
  181. let filters = getFilters();
  182. if (filters.length > 1) {
  183. elem.remove();
  184. filters = getFilters();
  185. filters[filters.length - 1].querySelector("#operation").disabled = true;
  186. }
  187. }
  188. function clearFilters() {
  189. let filtered = document.querySelectorAll(".filtered");
  190. filtered.forEach(f => f.classList.remove("filtered"))
  191. getFilters().forEach(f => f.remove());
  192. document.querySelector("#plancia").style.display = "none";
  193. }
  194. function makeDateFilter(criterion) {
  195. let start = criterion.querySelector("#start-date").value;
  196. let stop = criterion.querySelector("#stop-date").value;
  197. start = (start === '') ? new Date(0) : new Date(start);
  198. stop = (stop === '') ? new Date() : new Date(stop);
  199. return date => (date <= stop) && (date >= start);
  200. }
  201. function makeSeverityFilter(criterion) {
  202. let select = criterion.querySelector("#severity");
  203. let severity = select.options[select.selectedIndex].value;
  204. if (severity === "-1") {
  205. return x => true;
  206. }
  207. return x => x === severity;
  208. }
  209. function makeFilterFunction(criterion) {
  210. let dateFilter = makeDateFilter(criterion);
  211. let severityFilter = makeSeverityFilter(criterion);
  212. return notif => {
  213. let microtime = new Date(parseFloat(notif.dataset.microtime) * 1000);
  214. let severity = notif.dataset.severity;
  215. return dateFilter(microtime) && severityFilter(severity);
  216. }
  217. }
  218. function applyFilters() {
  219. let filters = getFilters();
  220. let notifs = getAliveNotifs();
  221. let filterFunctions = filters.map(makeFilterFunction);
  222. let fun = x => filterFunctions[0](x);
  223. for (let i = 0; i < filters.length - 1; i++) {
  224. // Questo hack serve per evitare che la chiamata
  225. // al filtro sia interpretata come ricorsione.
  226. let filter = fun;
  227. if (filters[i].querySelector("#operation").value === "0") {
  228. fun = x => (filter(x) && filterFunctions[i + 1](x));
  229. } else {
  230. fun = x => (filter(x) || filterFunctions[i + 1](x));
  231. }
  232. }
  233. notifs.forEach(notif => {
  234. if (!fun(notif)) {
  235. notif.classList.add("filtered");
  236. }
  237. });
  238. document.querySelector("#plancia").style.display = "none";
  239. }
  240. // -->
  241. </script>
  242. <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css"
  243. integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous">
  244. </head>
  245. <body>
  246. <nav>
  247. <div id="hmenu">
  248. <ul>
  249. <?php echo($menuout); ?>
  250. </ul>
  251. <div class="mtit">Notifiche</div>
  252. <div id="rightdiv">
  253. <img src="<?php echo($filtordimgoff); ?>" id="lente" class="rlinks" title="Mostra il pannello di filtraggio"
  254. onclick="showFilterPanel();">
  255. <?php echo('<img src="' . $notifs['imgoff'] . '" id="bell" class="rlinks" title="Show notifications" onclick="shidenotifs();">' . N); ?>
  256. <img src="imgs/esci.svg" class="rlinks" title="Logout" onclick="document.location.href='logout.php';">
  257. </div>
  258. </div>
  259. </nav>
  260. <?php echo($notifs['div']); ?>
  261. <div id="notifs-grid">
  262. <div id="notifs-list">
  263. <?php
  264. $sev_classes = array(
  265. 1 => "sev-normal",
  266. 2 => "sev-warning",
  267. 3 => "sev-error"
  268. );
  269. $seen_class = array(
  270. 0 => "unseen",
  271. 1 => "seen"
  272. );
  273. $deleted = array(
  274. 0 => "",
  275. 1 => "deleted"
  276. );
  277. foreach ($notifs['notifs'] as $n) {
  278. echo("<div id=\"notif-" . $n["ID"] . "\" class=\"" . "notif " . $deleted[$n["Deleted"]] . " " . $sev_classes[$n["Severity"]] . " " . $seen_class[$n["Seen"]] .
  279. "\" data-microtime='" . $n['Microtime'] . "' " .
  280. "data-severity='" . $n['Severity'] . "'>" .
  281. "<input type=\"checkbox\" class=\"seen-checkbox\" onclick='notifSelect(this)'>" .
  282. "<button type='button' onclick='markread(this.parentElement)'><i class=\"fa fa-envelope-open-o\" aria-hidden=\"true\"></i></button>" .
  283. "<button type='button' onclick='markdeleted(this.parentElement)'><i class=\"fa fa-trash-o\" aria-hidden=\"true\"></i></button>" .
  284. "<p id='notif-text'>" . gmdate('d/m/Y H:i:s', round($n['Microtime'])) . ": " . $n["Notification"] . "</p>" .
  285. "</div>\n");
  286. }
  287. ?>
  288. </div>
  289. <div id="popup">
  290. <div id="inpopup">
  291. <div id="popupcont">
  292. ...
  293. </div>
  294. </div>
  295. </div>
  296. <div id="footer">
  297. <div id="notifs-list-controls">
  298. <input id="global-selector" type="checkbox" onclick="toggleState(this)">
  299. <button id='reverse-btn' type='button' class="control-btn" onclick="reverseSelection()">
  300. Inverti selezione
  301. </button>
  302. <button id='read-btn' type='button' class="control-btn" onclick="readSelected()">
  303. <i class="fa fa-envelope-open-o" aria-hidden="true"></i>
  304. </button>
  305. <button id='delete-btn' type='button' class="control-btn" onclick="deleteSelected()">
  306. <i class="fa fa-trash-o" aria-hidden="true"></i>
  307. </button>
  308. </div>
  309. </div>
  310. </div>
  311. <div id="plancia">
  312. <button id='clear-filters-btn' type='button'
  313. onclick="confirma('Attenzione!','<p>Confermi di voler rimuovere tutti i criteri di filtraggio?</p>','No','Si','','clearFilters();')">
  314. Rimuovi tutti i criteri di filtraggio
  315. </button>
  316. <div id="criterion-list"></div>
  317. <button id='apply-filters-btn' type='button' onclick="applyFilters()">
  318. Applica
  319. </button>
  320. </div>
  321. </body>
  322. </html>
  323. <?php
  324. mysqli_close($link);
  325. exit(0);
  326. ?>