]> git.wh0rd.org Git - tt-rss.git/blob - js/prefs.js
editSelectedFeeds: fix missing unset checkboxes properly
[tt-rss.git] / js / prefs.js
1 /* global dijit, __ */
2
3 let hotkey_prefix = false;
4 let hotkey_prefix_pressed = false;
5
6 let seq = "";
7
8 function notify_callback2(transport, sticky) {
9         notify_info(transport.responseText, sticky);
10 }
11
12 function updateFeedList() {
13
14         const user_search = $("feed_search");
15         let search = "";
16         if (user_search) { search = user_search.value; }
17
18         xhrPost("backend.php", { op: "pref-feeds", search: search }, (transport) => {
19                 dijit.byId('feedConfigTab').attr('content', transport.responseText);
20                 selectTab("feedConfig", true);
21                 notify("");
22         });
23 }
24
25 function checkInactiveFeeds() {
26         xhrPost("backend.php", { op: "pref-feeds", method: "getinactivefeeds" }, (transport) => {
27                 if (parseInt(transport.responseText) > 0) {
28                         Element.show(dijit.byId("pref_feeds_inactive_btn").domNode);
29                 }
30         });
31 }
32
33 function updateUsersList(sort_key) {
34         const user_search = $("user_search");
35         const search = user_search ? user_search.value : "";
36
37         const query = { op: "pref-users", sort:  sort_key, search: search };
38
39         xhrPost("backend.php", query, (transport) => {
40                 dijit.byId('userConfigTab').attr('content', transport.responseText);
41                 selectTab("userConfig", true)
42                 notify("");
43         });
44 }
45
46 function addUser() {
47         const login = prompt(__("Please enter login:"), "");
48
49         if (login == null) {
50                 return false;
51         }
52
53         if (login == "") {
54                 alert(__("Can't create user: no login specified."));
55                 return false;
56         }
57
58         notify_progress("Adding user...");
59
60         xhrPost("backend.php", { op: "pref-users", method: "add", login: login }, (transport) => {
61         notify_callback2(transport);
62         updateUsersList();
63     });
64
65 }
66
67 function editUser(id) {
68
69         const query = "backend.php?op=pref-users&method=edit&id=" +
70                 param_escape(id);
71
72         if (dijit.byId("userEditDlg"))
73                 dijit.byId("userEditDlg").destroyRecursive();
74
75         const dialog = new dijit.Dialog({
76                 id: "userEditDlg",
77                 title: __("User Editor"),
78                 style: "width: 600px",
79                 execute: function () {
80                         if (this.validate()) {
81                                 notify_progress("Saving data...", true);
82
83                                 xhrPost("backend.php", dojo.formToObject("user_edit_form"), (transport) => {
84                     dialog.hide();
85                     updateUsersList();
86                 });
87                         }
88                 },
89                 href: query
90         });
91
92         dialog.show();
93 }
94
95 function editFilter(id) {
96
97         const query = "backend.php?op=pref-filters&method=edit&id=" + param_escape(id);
98
99         if (dijit.byId("feedEditDlg"))
100                 dijit.byId("feedEditDlg").destroyRecursive();
101
102         if (dijit.byId("filterEditDlg"))
103                 dijit.byId("filterEditDlg").destroyRecursive();
104
105         const dialog = new dijit.Dialog({
106                 id: "filterEditDlg",
107                 title: __("Edit Filter"),
108                 style: "width: 600px",
109
110                 test: function () {
111                         const query = "backend.php?" + dojo.formToQuery("filter_edit_form") + "&savemode=test";
112
113                         editFilterTest(query);
114                 },
115                 selectRules: function (select) {
116                         $$("#filterDlg_Matches input[type=checkbox]").each(function (e) {
117                                 e.checked = select;
118                                 if (select)
119                                         e.parentNode.addClassName("Selected");
120                                 else
121                                         e.parentNode.removeClassName("Selected");
122                         });
123                 },
124                 selectActions: function (select) {
125                         $$("#filterDlg_Actions input[type=checkbox]").each(function (e) {
126                                 e.checked = select;
127
128                                 if (select)
129                                         e.parentNode.addClassName("Selected");
130                                 else
131                                         e.parentNode.removeClassName("Selected");
132
133                         });
134                 },
135                 editRule: function (e) {
136                         const li = e.parentNode;
137                         const rule = li.getElementsByTagName("INPUT")[1].value;
138                         addFilterRule(li, rule);
139                 },
140                 editAction: function (e) {
141                         const li = e.parentNode;
142                         const action = li.getElementsByTagName("INPUT")[1].value;
143                         addFilterAction(li, action);
144                 },
145                 removeFilter: function () {
146                         const msg = __("Remove filter?");
147
148                         if (confirm(msg)) {
149                                 this.hide();
150
151                                 notify_progress("Removing filter...");
152
153                                 const query = { op: "pref-filters", method: "remove", ids: this.attr('value').id };
154
155                                 xhrPost("backend.php", query, () => {
156                                         updateFilterList();
157                                 });
158                         }
159                 },
160                 addAction: function () {
161                         addFilterAction();
162                 },
163                 addRule: function () {
164                         addFilterRule();
165                 },
166                 deleteAction: function () {
167                         $$("#filterDlg_Actions li[class*=Selected]").each(function (e) {
168                                 e.parentNode.removeChild(e)
169                         });
170                 },
171                 deleteRule: function () {
172                         $$("#filterDlg_Matches li[class*=Selected]").each(function (e) {
173                                 e.parentNode.removeChild(e)
174                         });
175                 },
176                 execute: function () {
177                         if (this.validate()) {
178
179                                 notify_progress("Saving data...", true);
180
181                                 xhrPost("backend.php", dojo.formToObject("filter_edit_form"), () => {
182                                         dialog.hide();
183                                         updateFilterList();
184                                 });
185                         }
186                 },
187                 href: query
188         });
189
190         dialog.show();
191 }
192
193
194 function getSelectedLabels() {
195         const tree = dijit.byId("labelTree");
196         const items = tree.model.getCheckedItems();
197         const rv = [];
198
199         items.each(function(item) {
200                 rv.push(tree.model.store.getValue(item, 'bare_id'));
201         });
202
203         return rv;
204 }
205
206 function getSelectedUsers() {
207         return getSelectedTableRowIds("prefUserList");
208 }
209
210 function getSelectedFeeds() {
211         const tree = dijit.byId("feedTree");
212         const items = tree.model.getCheckedItems();
213         const rv = [];
214
215         items.each(function(item) {
216                 if (item.id[0].match("FEED:"))
217                         rv.push(tree.model.store.getValue(item, 'bare_id'));
218         });
219
220         return rv;
221 }
222
223 function getSelectedCategories() {
224         const tree = dijit.byId("feedTree");
225         const items = tree.model.getCheckedItems();
226         const rv = [];
227
228         items.each(function(item) {
229                 if (item.id[0].match("CAT:"))
230                         rv.push(tree.model.store.getValue(item, 'bare_id'));
231         });
232
233         return rv;
234 }
235
236 function getSelectedFilters() {
237         const tree = dijit.byId("filterTree");
238         const items = tree.model.getCheckedItems();
239         const rv = [];
240
241         items.each(function(item) {
242                 rv.push(tree.model.store.getValue(item, 'bare_id'));
243         });
244
245         return rv;
246
247 }
248
249 function removeSelectedLabels() {
250
251         const sel_rows = getSelectedLabels();
252
253         if (sel_rows.length > 0) {
254
255                 const ok = confirm(__("Remove selected labels?"));
256
257                 if (ok) {
258                         notify_progress("Removing selected labels...");
259
260                         const query = { op: "pref-labels", method: "remove",
261                                 ids: sel_rows.toString() };
262
263                         xhrPost("backend.php",  query, () => {
264                                 updateLabelList();
265                         });
266                 }
267         } else {
268                 alert(__("No labels are selected."));
269         }
270
271         return false;
272 }
273
274 function removeSelectedUsers() {
275
276         const sel_rows = getSelectedUsers();
277
278         if (sel_rows.length > 0) {
279
280                 const ok = confirm(__("Remove selected users? Neither default admin nor your account will be removed."));
281
282                 if (ok) {
283                         notify_progress("Removing selected users...");
284
285                         const query = { op: "pref-users", method: "remove",
286                                 ids: sel_rows.toString() };
287
288                         xhrPost("backend.php", query, () => {
289                                 updateUsersList();
290                         });
291                 }
292
293         } else {
294                 alert(__("No users are selected."));
295         }
296
297         return false;
298 }
299
300 function removeSelectedFilters() {
301
302         const sel_rows = getSelectedFilters();
303
304         if (sel_rows.length > 0) {
305
306                 const ok = confirm(__("Remove selected filters?"));
307
308                 if (ok) {
309                         notify_progress("Removing selected filters...");
310
311                         const query = { op: "pref-filters", method: "remove",
312                                 ids:  sel_rows.toString() };
313
314                         xhrPost("backend.php", query, () => {
315                                 updateFilterList();
316                         });
317                 }
318         } else {
319                 alert(__("No filters are selected."));
320         }
321
322         return false;
323 }
324
325 function removeSelectedFeeds() {
326
327         const sel_rows = getSelectedFeeds();
328
329         if (sel_rows.length > 0) {
330
331                 const ok = confirm(__("Unsubscribe from selected feeds?"));
332
333                 if (ok) {
334
335                         notify_progress("Unsubscribing from selected feeds...", true);
336
337                         const query = { op: "pref-feeds", method: "remove",
338                                 ids: sel_rows.toString() };
339
340                         xhrPost("backend.php", query, () => {
341                                 updateFeedList();
342                         });
343                 }
344
345         } else {
346                 alert(__("No feeds are selected."));
347         }
348
349         return false;
350 }
351
352 function editSelectedUser() {
353         const rows = getSelectedUsers();
354
355         if (rows.length == 0) {
356                 alert(__("No users are selected."));
357                 return;
358         }
359
360         if (rows.length > 1) {
361                 alert(__("Please select only one user."));
362                 return;
363         }
364
365         notify("");
366
367         editUser(rows[0]);
368 }
369
370 function resetSelectedUserPass() {
371
372         const rows = getSelectedUsers();
373
374         if (rows.length == 0) {
375                 alert(__("No users are selected."));
376                 return;
377         }
378
379         if (rows.length > 1) {
380                 alert(__("Please select only one user."));
381                 return;
382         }
383
384         const ok = confirm(__("Reset password of selected user?"));
385
386         if (ok) {
387                 notify_progress("Resetting password for selected user...");
388
389                 const id = rows[0];
390
391                 xhrPost("backend.php", { op: "pref-users", method: "resetPass", id: id }, (transport) => {
392                         notify_info(transport.responseText, true);
393                 });
394
395         }
396 }
397
398 function selectedUserDetails() {
399
400         const rows = getSelectedUsers();
401
402         if (rows.length == 0) {
403                 alert(__("No users are selected."));
404                 return;
405         }
406
407         if (rows.length > 1) {
408                 alert(__("Please select only one user."));
409                 return;
410         }
411
412         const query = "backend.php?op=pref-users&method=userdetails&id=" + param_escape(rows[0]);
413
414         if (dijit.byId("userDetailsDlg"))
415                 dijit.byId("userDetailsDlg").destroyRecursive();
416
417         const dialog = new dijit.Dialog({
418                 id: "userDetailsDlg",
419                 title: __("User details"),
420                 style: "width: 600px",
421                 execute: function () {
422                         dialog.hide();
423                 },
424                 href: query
425         });
426
427         dialog.show();
428 }
429
430
431 function editSelectedFilter() {
432         const rows = getSelectedFilters();
433
434         if (rows.length == 0) {
435                 alert(__("No filters are selected."));
436                 return;
437         }
438
439         if (rows.length > 1) {
440                 alert(__("Please select only one filter."));
441                 return;
442         }
443
444         notify("");
445
446         editFilter(rows[0]);
447
448 }
449
450 function joinSelectedFilters() {
451         const rows = getSelectedFilters();
452
453         if (rows.length == 0) {
454                 alert(__("No filters are selected."));
455                 return;
456         }
457
458         const ok = confirm(__("Combine selected filters?"));
459
460         if (ok) {
461                 notify_progress("Joining filters...");
462
463                 xhrPost("backend.php", { op: "pref-filters", method: "join", ids: rows.toString() }, () => {
464                         updateFilterList();
465                 });
466         }
467 }
468
469 function editSelectedFeed() {
470         const rows = getSelectedFeeds();
471
472         if (rows.length == 0) {
473                 alert(__("No feeds are selected."));
474                 return;
475         }
476
477         if (rows.length > 1) {
478                 return editSelectedFeeds();
479         }
480
481         notify("");
482
483         editFeed(rows[0], {});
484
485 }
486
487 function editSelectedFeeds() {
488         const rows = getSelectedFeeds();
489
490         if (rows.length == 0) {
491                 alert(__("No feeds are selected."));
492                 return;
493         }
494
495         notify_progress("Loading, please wait...");
496
497         const query = "backend.php?op=pref-feeds&method=editfeeds&ids=" +
498                 param_escape(rows.toString());
499
500         console.log(query);
501
502         if (dijit.byId("feedEditDlg"))
503                 dijit.byId("feedEditDlg").destroyRecursive();
504
505         new Ajax.Request("backend.php", {
506                 parameters: query,
507                 onComplete: function (transport) {
508
509                         notify("");
510
511                         const dialog = new dijit.Dialog({
512                                 id: "feedEditDlg",
513                                 title: __("Edit Multiple Feeds"),
514                                 style: "width: 600px",
515                                 getChildByName: function (name) {
516                                         let rv = null;
517                                         this.getChildren().each(
518                                                 function (child) {
519                                                         if (child.name == name) {
520                                                                 rv = child;
521                                                                 return;
522                                                         }
523                                                 });
524                                         return rv;
525                                 },
526                                 toggleField: function (checkbox, elem, label) {
527                                         this.getChildByName(elem).attr('disabled', !checkbox.checked);
528
529                                         if ($(label))
530                                                 if (checkbox.checked)
531                                                         $(label).removeClassName('insensitive');
532                                                 else
533                                                         $(label).addClassName('insensitive');
534
535                                 },
536                                 execute: function () {
537                                         if (this.validate() && confirm(__("Save changes to selected feeds?"))) {
538                                                 const query = this.attr('value');
539
540                                                 /* normalize unchecked checkboxes because [] is not serialized */
541
542                                                 Object.keys(query).each((key) => {
543                                                         let val = query[key];
544
545                                                         if (typeof val == "object" && val.length == 0)
546                                                                 query[key] = ["off"];
547                                                 });
548
549                                                 notify_progress("Saving data...", true);
550
551                                                 xhrPost("backend.php", query, () => {
552                                                         dialog.hide();
553                                                         updateFeedList();
554                                                 });
555                                         }
556                                 },
557                                 content: transport.responseText
558                         });
559
560                         dialog.show();
561
562                 }
563         });
564 }
565
566 function opmlImportComplete(iframe) {
567         if (!iframe.contentDocument.body.innerHTML) return false;
568
569         Element.show(iframe);
570
571         notify('');
572
573         if (dijit.byId('opmlImportDlg'))
574                 dijit.byId('opmlImportDlg').destroyRecursive();
575
576         const content = iframe.contentDocument.body.innerHTML;
577
578         const dialog = new dijit.Dialog({
579                 id: "opmlImportDlg",
580                 title: __("OPML Import"),
581                 style: "width: 600px",
582                 onCancel: function () {
583             window.location.reload();
584                 },
585                 execute: function () {
586                         window.location.reload();
587                 },
588                 content: content
589         });
590
591         dialog.show();
592 }
593
594 function opmlImport() {
595
596         const opml_file = $("opml_file");
597
598         if (opml_file.value.length == 0) {
599                 alert(__("Please choose an OPML file first."));
600                 return false;
601         } else {
602                 notify_progress("Importing, please wait...", true);
603
604                 Element.show("upload_iframe");
605
606                 return true;
607         }
608 }
609
610
611 function updateFilterList() {
612         const user_search = $("filter_search");
613         let search = "";
614         if (user_search) { search = user_search.value; }
615
616         new Ajax.Request("backend.php", {
617                 parameters: "?op=pref-filters&search=" + param_escape(search),
618                 onComplete: function(transport) {
619                         dijit.byId('filterConfigTab').attr('content', transport.responseText);
620                         notify("");
621                 } });
622 }
623
624 function updateLabelList() {
625         new Ajax.Request("backend.php", {
626                 parameters: "?op=pref-labels",
627                 onComplete: function(transport) {
628                         dijit.byId('labelConfigTab').attr('content', transport.responseText);
629                         notify("");
630                 } });
631 }
632
633 function updatePrefsList() {
634         new Ajax.Request("backend.php", {
635                 parameters: "?op=pref-prefs",
636                 onComplete: function(transport) {
637                         dijit.byId('genConfigTab').attr('content', transport.responseText);
638                         notify("");
639                 } });
640 }
641
642 function updateSystemList() {
643         new Ajax.Request("backend.php", {
644                 parameters: "?op=pref-system",
645                 onComplete: function(transport) {
646                         dijit.byId('systemConfigTab').attr('content', transport.responseText);
647                         notify("");
648                 } });
649 }
650
651 function selectTab(id, noupdate) {
652         if (!noupdate) {
653                 notify_progress("Loading, please wait...");
654
655                 if (id == "feedConfig") {
656                         updateFeedList();
657                 } else if (id == "filterConfig") {
658                         updateFilterList();
659                 } else if (id == "labelConfig") {
660                         updateLabelList();
661                 } else if (id == "genConfig") {
662                         updatePrefsList();
663                 } else if (id == "userConfig") {
664                         updateUsersList();
665                 } else if (id == "systemConfig") {
666                         updateSystemList();
667                 }
668
669                 const tab = dijit.byId(id + "Tab");
670                 dijit.byId("pref-tabs").selectChild(tab);
671
672         }
673 }
674
675 function init_second_stage() {
676         document.onkeydown = pref_hotkey_handler;
677         loading_set_progress(50);
678         notify("");
679
680         let tab = getURLParam('tab');
681
682         if (tab) {
683                 tab = dijit.byId(tab + "Tab");
684                 if (tab) dijit.byId("pref-tabs").selectChild(tab);
685         }
686
687         const method = getURLParam('method');
688
689         if (method == 'editFeed') {
690                 const param = getURLParam('methodparam');
691
692                 window.setTimeout(function() { editFeed(param) }, 100);
693         }
694
695         setTimeout(hotkey_prefix_timeout, 5*1000);
696 }
697
698 function init() {
699         window.onerror = function (message, filename, lineno, colno, error) {
700                 report_error(message, filename, lineno, colno, error);
701         };
702
703         require(["dojo/_base/kernel",
704                 "dojo/ready",
705                 "dojo/parser",
706                 "dojo/_base/loader",
707                 "dojo/_base/html",
708                 "dijit/ColorPalette",
709                 "dijit/Dialog",
710                 "dijit/form/Button",
711                 "dijit/form/CheckBox",
712                 "dijit/form/DropDownButton",
713                 "dijit/form/FilteringSelect",
714         "dijit/form/MultiSelect",
715                 "dijit/form/Form",
716                 "dijit/form/RadioButton",
717                 "dijit/form/ComboButton",
718                 "dijit/form/Select",
719                 "dijit/form/SimpleTextarea",
720                 "dijit/form/TextBox",
721                 "dijit/form/ValidationTextBox",
722                 "dijit/InlineEditBox",
723                 "dijit/layout/AccordionContainer",
724                 "dijit/layout/AccordionPane",
725                 "dijit/layout/BorderContainer",
726                 "dijit/layout/ContentPane",
727                 "dijit/layout/TabContainer",
728                 "dijit/Menu",
729                 "dijit/ProgressBar",
730                 "dijit/Toolbar",
731                 "dijit/Tree",
732                 "dijit/tree/dndSource",
733                 "dojo/data/ItemFileWriteStore",
734                 "lib/CheckBoxStoreModel",
735                 "lib/CheckBoxTree",
736                 "fox/PrefFeedStore",
737                 "fox/PrefFilterStore",
738                 "fox/PrefFeedTree",
739                 "fox/PrefFilterTree",
740                 "fox/PrefLabelTree"], function (dojo, ready, parser) {
741
742                 ready(function () {
743                         try {
744                                 parser.parse();
745
746                                 loading_set_progress(50);
747
748                                 const clientTzOffset = new Date().getTimezoneOffset() * 60;
749
750                                 new Ajax.Request("backend.php", {
751                                         parameters: {
752                                                 op: "rpc", method: "sanityCheck",
753                                                 clientTzOffset: clientTzOffset
754                                         },
755                                         onComplete: function (transport) {
756                                                 backend_sanity_check_callback(transport);
757                                         }
758                                 });
759                         } catch (e) {
760                                 exception_error(e);
761                         }
762                 });
763         });
764 }
765
766
767 function validatePrefsReset() {
768         const ok = confirm(__("Reset to defaults?"));
769
770         if (ok) {
771
772                 const query = "?op=pref-prefs&method=resetconfig";
773                 console.log(query);
774
775                 new Ajax.Request("backend.php", {
776                         parameters: query,
777                         onComplete: function(transport) {
778                                 updatePrefsList();
779                                 notify_info(transport.responseText);
780                         } });
781
782         }
783
784         return false;
785
786 }
787
788 function pref_hotkey_handler(e) {
789
790         if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
791
792         let keycode = false;
793         let shift_key = false;
794
795         const cmdline = $('cmdline');
796
797         try {
798                 shift_key = e.shiftKey;
799         } catch (e) {
800
801         }
802
803         if (window.event) {
804                 keycode = window.event.keyCode;
805         } else if (e) {
806                 keycode = e.which;
807         }
808
809         let keychar = String.fromCharCode(keycode);
810
811         if (keycode == 27) { // escape
812                 hotkey_prefix = false;
813         }
814
815         if (keycode == 16) return; // ignore lone shift
816         if (keycode == 17) return; // ignore lone ctrl
817
818         if (!shift_key) keychar = keychar.toLowerCase();
819
820         var hotkeys = getInitParam("hotkeys");
821
822         if (!hotkey_prefix && hotkeys[0].indexOf(keychar) != -1) {
823
824                 const date = new Date();
825                 const ts = Math.round(date.getTime() / 1000);
826
827                 hotkey_prefix = keychar;
828                 hotkey_prefix_pressed = ts;
829
830                 cmdline.innerHTML = keychar;
831                 Element.show(cmdline);
832
833                 return true;
834         }
835
836         Element.hide(cmdline);
837
838         let hotkey = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
839         hotkey = hotkey_prefix ? hotkey_prefix + " " + hotkey : hotkey;
840         hotkey_prefix = false;
841
842         let hotkey_action = false;
843         var hotkeys = getInitParam("hotkeys");
844
845         for (const sequence in hotkeys[1]) {
846                 if (sequence == hotkey) {
847                         hotkey_action = hotkeys[1][sequence];
848                         break;
849                 }
850         }
851
852         switch (hotkey_action) {
853                 case "feed_subscribe":
854                         quickAddFeed();
855                         return false;
856                 case "create_label":
857                         addLabel();
858                         return false;
859                 case "create_filter":
860                         quickAddFilter();
861                         return false;
862                 case "help_dialog":
863                         //helpDialog("prefs");
864                         return false;
865                 default:
866                         console.log("unhandled action: " + hotkey_action + "; hotkey: " + hotkey);
867         }
868 }
869
870 function removeCategory(id, item) {
871
872         const ok = confirm(__("Remove category %s? Any nested feeds would be placed into Uncategorized.").replace("%s", item.name));
873
874         if (ok) {
875                 const query = "?op=pref-feeds&method=removeCat&ids=" +
876                         param_escape(id);
877
878                 notify_progress("Removing category...");
879
880                 new Ajax.Request("backend.php", {
881                         parameters: query,
882                         onComplete: function (transport) {
883                                 notify('');
884                                 updateFeedList();
885                         }
886                 });
887         }
888 }
889
890 function removeSelectedCategories() {
891
892         const sel_rows = getSelectedCategories();
893
894         if (sel_rows.length > 0) {
895
896                 const ok = confirm(__("Remove selected categories?"));
897
898                 if (ok) {
899                         notify_progress("Removing selected categories...");
900
901                         const query = "?op=pref-feeds&method=removeCat&ids="+
902                                 param_escape(sel_rows.toString());
903
904                         new Ajax.Request("backend.php", {
905                                 parameters: query,
906                                 onComplete: function(transport) {
907                                                 updateFeedList();
908                                         } });
909
910                 }
911         } else {
912                 alert(__("No categories are selected."));
913         }
914
915         return false;
916 }
917
918 function createCategory() {
919         const title = prompt(__("Category title:"));
920
921         if (title) {
922
923                 notify_progress("Creating category...");
924
925                 const query = "?op=pref-feeds&method=addCat&cat=" +
926                         param_escape(title);
927
928                 new Ajax.Request("backend.php", {
929                         parameters: query,
930                         onComplete: function (transport) {
931                                 notify('');
932                                 updateFeedList();
933                         }
934                 });
935         }
936 }
937
938 function showInactiveFeeds() {
939         const query = "backend.php?op=pref-feeds&method=inactiveFeeds";
940
941         if (dijit.byId("inactiveFeedsDlg"))
942                 dijit.byId("inactiveFeedsDlg").destroyRecursive();
943
944         const dialog = new dijit.Dialog({
945                 id: "inactiveFeedsDlg",
946                 title: __("Feeds without recent updates"),
947                 style: "width: 600px",
948                 getSelectedFeeds: function () {
949                         return getSelectedTableRowIds("prefInactiveFeedList");
950                 },
951                 removeSelected: function () {
952                         const sel_rows = this.getSelectedFeeds();
953
954                         console.log(sel_rows);
955
956                         if (sel_rows.length > 0) {
957                                 const ok = confirm(__("Remove selected feeds?"));
958
959                                 if (ok) {
960                                         notify_progress("Removing selected feeds...", true);
961
962                                         const query = "?op=pref-feeds&method=remove&ids=" +
963                                                 param_escape(sel_rows.toString());
964
965                                         new Ajax.Request("backend.php", {
966                                                 parameters: query,
967                                                 onComplete: function (transport) {
968                                                         notify('');
969                                                         dialog.hide();
970                                                         updateFeedList();
971                                                 }
972                                         });
973                                 }
974
975                         } else {
976                                 alert(__("No feeds are selected."));
977                         }
978                 },
979                 execute: function () {
980                         if (this.validate()) {
981                         }
982                 },
983                 href: query
984         });
985
986         dialog.show();
987 }
988
989 function opmlRegenKey() {
990         const ok = confirm(__("Replace current OPML publishing address with a new one?"));
991
992         if (ok) {
993
994                 notify_progress("Trying to change address...", true);
995
996                 const query = "?op=pref-feeds&method=regenOPMLKey";
997
998                 new Ajax.Request("backend.php", {
999                         parameters: query,
1000                         onComplete: function (transport) {
1001                                 const reply = JSON.parse(transport.responseText);
1002
1003                                 const new_link = reply.link;
1004
1005                                 const e = $('pub_opml_url');
1006
1007                                 if (new_link) {
1008                                         e.href = new_link;
1009                                         e.innerHTML = new_link;
1010
1011                                         new Effect.Highlight(e);
1012
1013                                         notify('');
1014
1015                                 } else {
1016                                         notify_error("Could not change feed URL.");
1017                                 }
1018                         }
1019                 });
1020         }
1021         return false;
1022 }
1023
1024 function labelColorReset() {
1025         const labels = getSelectedLabels();
1026
1027         if (labels.length > 0) {
1028                 const ok = confirm(__("Reset selected labels to default colors?"));
1029
1030                 if (ok) {
1031                         const query = "?op=pref-labels&method=colorreset&ids=" +
1032                                 param_escape(labels.toString());
1033
1034                         new Ajax.Request("backend.php", {
1035                                 parameters: query,
1036                                 onComplete: function (transport) {
1037                                         updateLabelList();
1038                                 }
1039                         });
1040                 }
1041
1042         } else {
1043                 alert(__("No labels are selected."));
1044         }
1045 }
1046
1047 function inPreferences() {
1048         return true;
1049 }
1050
1051 function editProfiles() {
1052
1053         if (dijit.byId("profileEditDlg"))
1054                 dijit.byId("profileEditDlg").destroyRecursive();
1055
1056         const query = "backend.php?op=pref-prefs&method=editPrefProfiles";
1057
1058         const dialog = new dijit.Dialog({
1059                 id: "profileEditDlg",
1060                 title: __("Settings Profiles"),
1061                 style: "width: 600px",
1062                 getSelectedProfiles: function () {
1063                         return getSelectedTableRowIds("prefFeedProfileList");
1064                 },
1065                 removeSelected: function () {
1066                         const sel_rows = this.getSelectedProfiles();
1067
1068                         if (sel_rows.length > 0) {
1069                                 const ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed."));
1070
1071                                 if (ok) {
1072                                         notify_progress("Removing selected profiles...", true);
1073
1074                                         const query = { op: "rpc", method: "remprofiles",
1075                                                 ids: sel_rows.toString() };
1076
1077                                         xhrPost("backend.php", query, () => {
1078                                                 notify('');
1079                                                 editProfiles();
1080                                         });
1081                                 }
1082
1083                         } else {
1084                                 alert(__("No profiles are selected."));
1085                         }
1086                 },
1087                 activateProfile: function () {
1088                         const sel_rows = this.getSelectedProfiles();
1089
1090                         if (sel_rows.length == 1) {
1091
1092                                 const ok = confirm(__("Activate selected profile?"));
1093
1094                                 if (ok) {
1095                                         notify_progress("Loading, please wait...");
1096
1097                     xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() },  () => {
1098                         window.location.reload();
1099                     });
1100                                 }
1101
1102                         } else {
1103                                 alert(__("Please choose a profile to activate."));
1104                         }
1105                 },
1106                 addProfile: function () {
1107                         if (this.validate()) {
1108                                 notify_progress("Creating profile...", true);
1109
1110                                 const query = { op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile };
1111
1112                                 xhrPost("backend.php", query, () => {
1113                                         notify('');
1114                                         editProfiles();
1115                                 });
1116
1117                         }
1118                 },
1119                 execute: function () {
1120                         if (this.validate()) {
1121                         }
1122                 },
1123                 href: query
1124         });
1125
1126         dialog.show();
1127 }
1128
1129 /*
1130 function activatePrefProfile() {
1131
1132         const sel_rows = getSelectedFeedCats();
1133
1134         if (sel_rows.length == 1) {
1135
1136                 const ok = confirm(__("Activate selected profile?"));
1137
1138                 if (ok) {
1139                         notify_progress("Loading, please wait...");
1140
1141                         xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() },  () => {
1142                                 window.location.reload();
1143                         });
1144                 }
1145
1146         } else {
1147                 alert(__("Please choose a profile to activate."));
1148         }
1149
1150         return false;
1151 } */
1152
1153 function clearFeedAccessKeys() {
1154
1155         const ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?"));
1156
1157         if (ok) {
1158                 notify_progress("Clearing URLs...");
1159
1160                 xhrPost("backend.php", { op: "pref-feeds", method: "clearKeys" }, () => {
1161             notify_info("Generated URLs cleared.");
1162         });
1163         }
1164
1165         return false;
1166 }
1167
1168 function resetFilterOrder() {
1169         notify_progress("Loading, please wait...");
1170
1171     xhrPost("backend.php", { op: "pref-filters", method: "filtersortreset" }, () => {
1172         updateFilterList();
1173     });
1174 }
1175
1176
1177 function resetFeedOrder() {
1178         notify_progress("Loading, please wait...");
1179
1180     xhrPost("backend.php", { op: "pref-feeds", method: "feedsortreset" }, () => {
1181         updateFeedList();
1182     });
1183 }
1184
1185 function resetCatOrder() {
1186         notify_progress("Loading, please wait...");
1187
1188         xhrPost("backend.php", { op: "pref-feeds", method: "catsortreset" }, () => {
1189                 updateFeedList();
1190         });
1191 }
1192
1193 function editCat(id, item) {
1194         const new_name = prompt(__('Rename category to:'), item.name);
1195
1196         if (new_name && new_name != item.name) {
1197
1198                 notify_progress("Loading, please wait...");
1199
1200                 xhrPost("backend.php", { op: 'pref-feeds', method: 'renamecat', id: id, title: new_name }, () => {
1201                         updateFeedList();
1202                 });
1203         }
1204 }
1205
1206 function editLabel(id) {
1207         const query = "backend.php?op=pref-labels&method=edit&id=" +
1208                 param_escape(id);
1209
1210         if (dijit.byId("labelEditDlg"))
1211                 dijit.byId("labelEditDlg").destroyRecursive();
1212
1213         const dialog = new dijit.Dialog({
1214                 id: "labelEditDlg",
1215                 title: __("Label Editor"),
1216                 style: "width: 600px",
1217                 setLabelColor: function (id, fg, bg) {
1218
1219                         let kind = '';
1220                         let color = '';
1221
1222                         if (fg && bg) {
1223                                 kind = 'both';
1224                         } else if (fg) {
1225                                 kind = 'fg';
1226                                 color = fg;
1227                         } else if (bg) {
1228                                 kind = 'bg';
1229                                 color = bg;
1230                         }
1231
1232                         const e = $("LICID-" + id);
1233
1234                         if (e) {
1235                                 if (fg) e.style.color = fg;
1236                                 if (bg) e.style.backgroundColor = bg;
1237                         }
1238
1239             const query = { op: "pref-labels", method: "colorset", kind: kind,
1240                 ids: id, fg: fg, bg: bg, color: color };
1241
1242             xhrPost("backend.php", query, () => {
1243                 updateFilterList(); // maybe there's labels in there
1244                         });
1245
1246                 },
1247                 execute: function () {
1248                         if (this.validate()) {
1249                                 const caption = this.attr('value').caption;
1250                                 const fg_color = this.attr('value').fg_color;
1251                                 const bg_color = this.attr('value').bg_color;
1252
1253                                 dijit.byId('labelTree').setNameById(id, caption);
1254                                 this.setLabelColor(id, fg_color, bg_color);
1255                                 this.hide();
1256
1257                                 xhrPost("backend.php", this.attr('value'), () => {
1258                     updateFilterList(); // maybe there's labels in there
1259                                 });
1260                         }
1261                 },
1262                 href: query
1263         });
1264
1265         dialog.show();
1266 }
1267
1268
1269 function customizeCSS() {
1270         const query = "backend.php?op=pref-prefs&method=customizeCSS";
1271
1272         if (dijit.byId("cssEditDlg"))
1273                 dijit.byId("cssEditDlg").destroyRecursive();
1274
1275         const dialog = new dijit.Dialog({
1276                 id: "cssEditDlg",
1277                 title: __("Customize stylesheet"),
1278                 style: "width: 600px",
1279                 execute: function () {
1280                         notify_progress('Saving data...', true);
1281
1282                         xhrPost("backend.php", this.attr('value'), () => {
1283                                 window.location.reload();
1284                         });
1285
1286                 },
1287                 href: query
1288         });
1289
1290         dialog.show();
1291 }
1292
1293 function insertSSLserial(value) {
1294         dijit.byId("SSL_CERT_SERIAL").attr('value', value);
1295 }
1296
1297 function gotoExportOpml(filename, settings) {
1298         const tmp = settings ? 1 : 0;
1299         document.location.href = "backend.php?op=opml&method=export&filename=" + filename + "&settings=" + tmp;
1300 }
1301
1302
1303 function batchSubscribe() {
1304         const query = "backend.php?op=pref-feeds&method=batchSubscribe";
1305
1306         // overlapping widgets
1307         if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
1308         if (dijit.byId("feedAddDlg"))    dijit.byId("feedAddDlg").destroyRecursive();
1309
1310         const dialog = new dijit.Dialog({
1311                 id: "batchSubDlg",
1312                 title: __("Batch subscribe"),
1313                 style: "width: 600px",
1314                 execute: function () {
1315                         if (this.validate()) {
1316                                 notify_progress(__("Subscribing to feeds..."), true);
1317
1318                                 xhrPost("backend.php", this.attr('value'), () => {
1319                                         notify("");
1320                                         updateFeedList();
1321                                         dialog.hide();
1322                                 });
1323                         }
1324                 },
1325                 href: query
1326         });
1327
1328         dialog.show();
1329 }
1330
1331 function clearPluginData(name) {
1332         if (confirm(__("Clear stored data for this plugin?"))) {
1333                 notify_progress("Loading, please wait...");
1334
1335                 xhrPost("backend.php", { op: "pref-prefs", method: "clearplugindata", name: name }, () => {
1336                         notify('');
1337                         updatePrefsList();
1338                 });
1339         }
1340 }
1341
1342 function clearSqlLog() {
1343
1344         if (confirm(__("Clear all messages in the error log?"))) {
1345
1346                 notify_progress("Loading, please wait...");
1347
1348                 xhrPost("backend.php",  { op: "pref-system", method: "clearLog" }, () => {
1349                         updateSystemList();
1350                 });
1351
1352         }
1353 }
1354
1355 function updateSelectedPrompt() {
1356         // no-op shim for toggleSelectedRow()
1357 }
1358