]> git.wh0rd.org - tt-rss.git/blob - js/prefs.js
some more xhrPost refactoring (batchEditSave WIP)
[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 //console.log(query); return;
541
542 /* Form.serialize ignores unchecked checkboxes */
543
544 /*if (!query.match("&private=") &&
545 this.getChildByName('private').attr('disabled') == false) {
546 query.private = "false";
547 }
548
549 try {
550 if (!query.match("&cache_images=") &&
551 this.getChildByName('cache_images').attr('disabled') == false) {
552 query.cache_images = "false";
553 }
554 } catch (e) {
555 }
556
557 try {
558 if (!query.match("&hide_images=") &&
559 this.getChildByName('hide_images').attr('disabled') == false) {
560 query.hide_images = "false";
561 }
562 } catch (e) {
563 }
564
565 if (!query.match("&include_in_digest=") &&
566 this.getChildByName('include_in_digest').attr('disabled') == false) {
567 query.include_in_digest = "false";
568 }
569
570 if (!query.match("&always_display_enclosures=") &&
571 this.getChildByName('always_display_enclosures').attr('disabled') == false) {
572 query.always_display_enclosures = "false";
573 }
574
575 if (!query.match("&mark_unread_on_update=") &&
576 this.getChildByName('mark_unread_on_update').attr('disabled') == false) {
577 query.mark_unread_on_update = "false";
578 }*/
579
580 notify_progress("Saving data...", true);
581
582 xhrPost("backend.php", query, () => {
583 dialog.hide();
584 updateFeedList();
585 });
586 }
587 },
588 content: transport.responseText
589 });
590
591 dialog.show();
592
593 }
594 });
595 }
596
597 function opmlImportComplete(iframe) {
598 if (!iframe.contentDocument.body.innerHTML) return false;
599
600 Element.show(iframe);
601
602 notify('');
603
604 if (dijit.byId('opmlImportDlg'))
605 dijit.byId('opmlImportDlg').destroyRecursive();
606
607 const content = iframe.contentDocument.body.innerHTML;
608
609 const dialog = new dijit.Dialog({
610 id: "opmlImportDlg",
611 title: __("OPML Import"),
612 style: "width: 600px",
613 onCancel: function () {
614 window.location.reload();
615 },
616 execute: function () {
617 window.location.reload();
618 },
619 content: content
620 });
621
622 dialog.show();
623 }
624
625 function opmlImport() {
626
627 const opml_file = $("opml_file");
628
629 if (opml_file.value.length == 0) {
630 alert(__("Please choose an OPML file first."));
631 return false;
632 } else {
633 notify_progress("Importing, please wait...", true);
634
635 Element.show("upload_iframe");
636
637 return true;
638 }
639 }
640
641
642 function updateFilterList() {
643 const user_search = $("filter_search");
644 let search = "";
645 if (user_search) { search = user_search.value; }
646
647 new Ajax.Request("backend.php", {
648 parameters: "?op=pref-filters&search=" + param_escape(search),
649 onComplete: function(transport) {
650 dijit.byId('filterConfigTab').attr('content', transport.responseText);
651 notify("");
652 } });
653 }
654
655 function updateLabelList() {
656 new Ajax.Request("backend.php", {
657 parameters: "?op=pref-labels",
658 onComplete: function(transport) {
659 dijit.byId('labelConfigTab').attr('content', transport.responseText);
660 notify("");
661 } });
662 }
663
664 function updatePrefsList() {
665 new Ajax.Request("backend.php", {
666 parameters: "?op=pref-prefs",
667 onComplete: function(transport) {
668 dijit.byId('genConfigTab').attr('content', transport.responseText);
669 notify("");
670 } });
671 }
672
673 function updateSystemList() {
674 new Ajax.Request("backend.php", {
675 parameters: "?op=pref-system",
676 onComplete: function(transport) {
677 dijit.byId('systemConfigTab').attr('content', transport.responseText);
678 notify("");
679 } });
680 }
681
682 function selectTab(id, noupdate) {
683 if (!noupdate) {
684 notify_progress("Loading, please wait...");
685
686 if (id == "feedConfig") {
687 updateFeedList();
688 } else if (id == "filterConfig") {
689 updateFilterList();
690 } else if (id == "labelConfig") {
691 updateLabelList();
692 } else if (id == "genConfig") {
693 updatePrefsList();
694 } else if (id == "userConfig") {
695 updateUsersList();
696 } else if (id == "systemConfig") {
697 updateSystemList();
698 }
699
700 const tab = dijit.byId(id + "Tab");
701 dijit.byId("pref-tabs").selectChild(tab);
702
703 }
704 }
705
706 function init_second_stage() {
707 document.onkeydown = pref_hotkey_handler;
708 loading_set_progress(50);
709 notify("");
710
711 let tab = getURLParam('tab');
712
713 if (tab) {
714 tab = dijit.byId(tab + "Tab");
715 if (tab) dijit.byId("pref-tabs").selectChild(tab);
716 }
717
718 const method = getURLParam('method');
719
720 if (method == 'editFeed') {
721 const param = getURLParam('methodparam');
722
723 window.setTimeout(function() { editFeed(param) }, 100);
724 }
725
726 setTimeout(hotkey_prefix_timeout, 5*1000);
727 }
728
729 function init() {
730 window.onerror = function (message, filename, lineno, colno, error) {
731 report_error(message, filename, lineno, colno, error);
732 };
733
734 require(["dojo/_base/kernel",
735 "dojo/ready",
736 "dojo/parser",
737 "dojo/_base/loader",
738 "dojo/_base/html",
739 "dijit/ColorPalette",
740 "dijit/Dialog",
741 "dijit/form/Button",
742 "dijit/form/CheckBox",
743 "dijit/form/DropDownButton",
744 "dijit/form/FilteringSelect",
745 "dijit/form/MultiSelect",
746 "dijit/form/Form",
747 "dijit/form/RadioButton",
748 "dijit/form/ComboButton",
749 "dijit/form/Select",
750 "dijit/form/SimpleTextarea",
751 "dijit/form/TextBox",
752 "dijit/form/ValidationTextBox",
753 "dijit/InlineEditBox",
754 "dijit/layout/AccordionContainer",
755 "dijit/layout/AccordionPane",
756 "dijit/layout/BorderContainer",
757 "dijit/layout/ContentPane",
758 "dijit/layout/TabContainer",
759 "dijit/Menu",
760 "dijit/ProgressBar",
761 "dijit/Toolbar",
762 "dijit/Tree",
763 "dijit/tree/dndSource",
764 "dojo/data/ItemFileWriteStore",
765 "lib/CheckBoxStoreModel",
766 "lib/CheckBoxTree",
767 "fox/PrefFeedStore",
768 "fox/PrefFilterStore",
769 "fox/PrefFeedTree",
770 "fox/PrefFilterTree",
771 "fox/PrefLabelTree"], function (dojo, ready, parser) {
772
773 ready(function () {
774 try {
775 parser.parse();
776
777 loading_set_progress(50);
778
779 const clientTzOffset = new Date().getTimezoneOffset() * 60;
780
781 new Ajax.Request("backend.php", {
782 parameters: {
783 op: "rpc", method: "sanityCheck",
784 clientTzOffset: clientTzOffset
785 },
786 onComplete: function (transport) {
787 backend_sanity_check_callback(transport);
788 }
789 });
790 } catch (e) {
791 exception_error(e);
792 }
793 });
794 });
795 }
796
797
798 function validatePrefsReset() {
799 const ok = confirm(__("Reset to defaults?"));
800
801 if (ok) {
802
803 const query = "?op=pref-prefs&method=resetconfig";
804 console.log(query);
805
806 new Ajax.Request("backend.php", {
807 parameters: query,
808 onComplete: function(transport) {
809 updatePrefsList();
810 notify_info(transport.responseText);
811 } });
812
813 }
814
815 return false;
816
817 }
818
819 function pref_hotkey_handler(e) {
820
821 if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
822
823 let keycode = false;
824 let shift_key = false;
825
826 const cmdline = $('cmdline');
827
828 try {
829 shift_key = e.shiftKey;
830 } catch (e) {
831
832 }
833
834 if (window.event) {
835 keycode = window.event.keyCode;
836 } else if (e) {
837 keycode = e.which;
838 }
839
840 let keychar = String.fromCharCode(keycode);
841
842 if (keycode == 27) { // escape
843 hotkey_prefix = false;
844 }
845
846 if (keycode == 16) return; // ignore lone shift
847 if (keycode == 17) return; // ignore lone ctrl
848
849 if (!shift_key) keychar = keychar.toLowerCase();
850
851 var hotkeys = getInitParam("hotkeys");
852
853 if (!hotkey_prefix && hotkeys[0].indexOf(keychar) != -1) {
854
855 const date = new Date();
856 const ts = Math.round(date.getTime() / 1000);
857
858 hotkey_prefix = keychar;
859 hotkey_prefix_pressed = ts;
860
861 cmdline.innerHTML = keychar;
862 Element.show(cmdline);
863
864 return true;
865 }
866
867 Element.hide(cmdline);
868
869 let hotkey = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
870 hotkey = hotkey_prefix ? hotkey_prefix + " " + hotkey : hotkey;
871 hotkey_prefix = false;
872
873 let hotkey_action = false;
874 var hotkeys = getInitParam("hotkeys");
875
876 for (const sequence in hotkeys[1]) {
877 if (sequence == hotkey) {
878 hotkey_action = hotkeys[1][sequence];
879 break;
880 }
881 }
882
883 switch (hotkey_action) {
884 case "feed_subscribe":
885 quickAddFeed();
886 return false;
887 case "create_label":
888 addLabel();
889 return false;
890 case "create_filter":
891 quickAddFilter();
892 return false;
893 case "help_dialog":
894 //helpDialog("prefs");
895 return false;
896 default:
897 console.log("unhandled action: " + hotkey_action + "; hotkey: " + hotkey);
898 }
899 }
900
901 function removeCategory(id, item) {
902
903 const ok = confirm(__("Remove category %s? Any nested feeds would be placed into Uncategorized.").replace("%s", item.name));
904
905 if (ok) {
906 const query = "?op=pref-feeds&method=removeCat&ids=" +
907 param_escape(id);
908
909 notify_progress("Removing category...");
910
911 new Ajax.Request("backend.php", {
912 parameters: query,
913 onComplete: function (transport) {
914 notify('');
915 updateFeedList();
916 }
917 });
918 }
919 }
920
921 function removeSelectedCategories() {
922
923 const sel_rows = getSelectedCategories();
924
925 if (sel_rows.length > 0) {
926
927 const ok = confirm(__("Remove selected categories?"));
928
929 if (ok) {
930 notify_progress("Removing selected categories...");
931
932 const query = "?op=pref-feeds&method=removeCat&ids="+
933 param_escape(sel_rows.toString());
934
935 new Ajax.Request("backend.php", {
936 parameters: query,
937 onComplete: function(transport) {
938 updateFeedList();
939 } });
940
941 }
942 } else {
943 alert(__("No categories are selected."));
944 }
945
946 return false;
947 }
948
949 function createCategory() {
950 const title = prompt(__("Category title:"));
951
952 if (title) {
953
954 notify_progress("Creating category...");
955
956 const query = "?op=pref-feeds&method=addCat&cat=" +
957 param_escape(title);
958
959 new Ajax.Request("backend.php", {
960 parameters: query,
961 onComplete: function (transport) {
962 notify('');
963 updateFeedList();
964 }
965 });
966 }
967 }
968
969 function showInactiveFeeds() {
970 const query = "backend.php?op=pref-feeds&method=inactiveFeeds";
971
972 if (dijit.byId("inactiveFeedsDlg"))
973 dijit.byId("inactiveFeedsDlg").destroyRecursive();
974
975 const dialog = new dijit.Dialog({
976 id: "inactiveFeedsDlg",
977 title: __("Feeds without recent updates"),
978 style: "width: 600px",
979 getSelectedFeeds: function () {
980 return getSelectedTableRowIds("prefInactiveFeedList");
981 },
982 removeSelected: function () {
983 const sel_rows = this.getSelectedFeeds();
984
985 console.log(sel_rows);
986
987 if (sel_rows.length > 0) {
988 const ok = confirm(__("Remove selected feeds?"));
989
990 if (ok) {
991 notify_progress("Removing selected feeds...", true);
992
993 const query = "?op=pref-feeds&method=remove&ids=" +
994 param_escape(sel_rows.toString());
995
996 new Ajax.Request("backend.php", {
997 parameters: query,
998 onComplete: function (transport) {
999 notify('');
1000 dialog.hide();
1001 updateFeedList();
1002 }
1003 });
1004 }
1005
1006 } else {
1007 alert(__("No feeds are selected."));
1008 }
1009 },
1010 execute: function () {
1011 if (this.validate()) {
1012 }
1013 },
1014 href: query
1015 });
1016
1017 dialog.show();
1018 }
1019
1020 function opmlRegenKey() {
1021 const ok = confirm(__("Replace current OPML publishing address with a new one?"));
1022
1023 if (ok) {
1024
1025 notify_progress("Trying to change address...", true);
1026
1027 const query = "?op=pref-feeds&method=regenOPMLKey";
1028
1029 new Ajax.Request("backend.php", {
1030 parameters: query,
1031 onComplete: function (transport) {
1032 const reply = JSON.parse(transport.responseText);
1033
1034 const new_link = reply.link;
1035
1036 const e = $('pub_opml_url');
1037
1038 if (new_link) {
1039 e.href = new_link;
1040 e.innerHTML = new_link;
1041
1042 new Effect.Highlight(e);
1043
1044 notify('');
1045
1046 } else {
1047 notify_error("Could not change feed URL.");
1048 }
1049 }
1050 });
1051 }
1052 return false;
1053 }
1054
1055 function labelColorReset() {
1056 const labels = getSelectedLabels();
1057
1058 if (labels.length > 0) {
1059 const ok = confirm(__("Reset selected labels to default colors?"));
1060
1061 if (ok) {
1062 const query = "?op=pref-labels&method=colorreset&ids=" +
1063 param_escape(labels.toString());
1064
1065 new Ajax.Request("backend.php", {
1066 parameters: query,
1067 onComplete: function (transport) {
1068 updateLabelList();
1069 }
1070 });
1071 }
1072
1073 } else {
1074 alert(__("No labels are selected."));
1075 }
1076 }
1077
1078 function inPreferences() {
1079 return true;
1080 }
1081
1082 function editProfiles() {
1083
1084 if (dijit.byId("profileEditDlg"))
1085 dijit.byId("profileEditDlg").destroyRecursive();
1086
1087 const query = "backend.php?op=pref-prefs&method=editPrefProfiles";
1088
1089 const dialog = new dijit.Dialog({
1090 id: "profileEditDlg",
1091 title: __("Settings Profiles"),
1092 style: "width: 600px",
1093 getSelectedProfiles: function () {
1094 return getSelectedTableRowIds("prefFeedProfileList");
1095 },
1096 removeSelected: function () {
1097 const sel_rows = this.getSelectedProfiles();
1098
1099 if (sel_rows.length > 0) {
1100 const ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed."));
1101
1102 if (ok) {
1103 notify_progress("Removing selected profiles...", true);
1104
1105 const query = { op: "rpc", method: "remprofiles",
1106 ids: sel_rows.toString() };
1107
1108 xhrPost("backend.php", query, () => {
1109 notify('');
1110 editProfiles();
1111 });
1112 }
1113
1114 } else {
1115 alert(__("No profiles are selected."));
1116 }
1117 },
1118 activateProfile: function () {
1119 const sel_rows = this.getSelectedProfiles();
1120
1121 if (sel_rows.length == 1) {
1122
1123 const ok = confirm(__("Activate selected profile?"));
1124
1125 if (ok) {
1126 notify_progress("Loading, please wait...");
1127
1128 xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() }, () => {
1129 window.location.reload();
1130 });
1131 }
1132
1133 } else {
1134 alert(__("Please choose a profile to activate."));
1135 }
1136 },
1137 addProfile: function () {
1138 if (this.validate()) {
1139 notify_progress("Creating profile...", true);
1140
1141 const query = { op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile };
1142
1143 xhrPost("backend.php", query, () => {
1144 notify('');
1145 editProfiles();
1146 });
1147
1148 }
1149 },
1150 execute: function () {
1151 if (this.validate()) {
1152 }
1153 },
1154 href: query
1155 });
1156
1157 dialog.show();
1158 }
1159
1160 /*
1161 function activatePrefProfile() {
1162
1163 const sel_rows = getSelectedFeedCats();
1164
1165 if (sel_rows.length == 1) {
1166
1167 const ok = confirm(__("Activate selected profile?"));
1168
1169 if (ok) {
1170 notify_progress("Loading, please wait...");
1171
1172 xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() }, () => {
1173 window.location.reload();
1174 });
1175 }
1176
1177 } else {
1178 alert(__("Please choose a profile to activate."));
1179 }
1180
1181 return false;
1182 } */
1183
1184 function clearFeedAccessKeys() {
1185
1186 const ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?"));
1187
1188 if (ok) {
1189 notify_progress("Clearing URLs...");
1190
1191 xhrPost("backend.php", { op: "pref-feeds", method: "clearKeys" }, () => {
1192 notify_info("Generated URLs cleared.");
1193 });
1194 }
1195
1196 return false;
1197 }
1198
1199 function resetFilterOrder() {
1200 notify_progress("Loading, please wait...");
1201
1202 xhrPost("backend.php", { op: "pref-filters", method: "filtersortreset" }, () => {
1203 updateFilterList();
1204 });
1205 }
1206
1207
1208 function resetFeedOrder() {
1209 notify_progress("Loading, please wait...");
1210
1211 xhrPost("backend.php", { op: "pref-feeds", method: "feedsortreset" }, () => {
1212 updateFeedList();
1213 });
1214 }
1215
1216 function resetCatOrder() {
1217 notify_progress("Loading, please wait...");
1218
1219 xhrPost("backend.php", { op: "pref-feeds", method: "catsortreset" }, () => {
1220 updateFeedList();
1221 });
1222 }
1223
1224 function editCat(id, item) {
1225 const new_name = prompt(__('Rename category to:'), item.name);
1226
1227 if (new_name && new_name != item.name) {
1228
1229 notify_progress("Loading, please wait...");
1230
1231 xhrPost("backend.php", { op: 'pref-feeds', method: 'renamecat', id: id, title: new_name }, () => {
1232 updateFeedList();
1233 });
1234 }
1235 }
1236
1237 function editLabel(id) {
1238 const query = "backend.php?op=pref-labels&method=edit&id=" +
1239 param_escape(id);
1240
1241 if (dijit.byId("labelEditDlg"))
1242 dijit.byId("labelEditDlg").destroyRecursive();
1243
1244 const dialog = new dijit.Dialog({
1245 id: "labelEditDlg",
1246 title: __("Label Editor"),
1247 style: "width: 600px",
1248 setLabelColor: function (id, fg, bg) {
1249
1250 let kind = '';
1251 let color = '';
1252
1253 if (fg && bg) {
1254 kind = 'both';
1255 } else if (fg) {
1256 kind = 'fg';
1257 color = fg;
1258 } else if (bg) {
1259 kind = 'bg';
1260 color = bg;
1261 }
1262
1263 const e = $("LICID-" + id);
1264
1265 if (e) {
1266 if (fg) e.style.color = fg;
1267 if (bg) e.style.backgroundColor = bg;
1268 }
1269
1270 const query = { op: "pref-labels", method: "colorset", kind: kind,
1271 ids: id, fg: fg, bg: bg, color: color };
1272
1273 xhrPost("backend.php", query, () => {
1274 updateFilterList(); // maybe there's labels in there
1275 });
1276
1277 },
1278 execute: function () {
1279 if (this.validate()) {
1280 const caption = this.attr('value').caption;
1281 const fg_color = this.attr('value').fg_color;
1282 const bg_color = this.attr('value').bg_color;
1283
1284 dijit.byId('labelTree').setNameById(id, caption);
1285 this.setLabelColor(id, fg_color, bg_color);
1286 this.hide();
1287
1288 xhrPost("backend.php", this.attr('value'), () => {
1289 updateFilterList(); // maybe there's labels in there
1290 });
1291 }
1292 },
1293 href: query
1294 });
1295
1296 dialog.show();
1297 }
1298
1299
1300 function customizeCSS() {
1301 const query = "backend.php?op=pref-prefs&method=customizeCSS";
1302
1303 if (dijit.byId("cssEditDlg"))
1304 dijit.byId("cssEditDlg").destroyRecursive();
1305
1306 const dialog = new dijit.Dialog({
1307 id: "cssEditDlg",
1308 title: __("Customize stylesheet"),
1309 style: "width: 600px",
1310 execute: function () {
1311 notify_progress('Saving data...', true);
1312
1313 xhrPost("backend.php", this.attr('value'), () => {
1314 window.location.reload();
1315 });
1316
1317 },
1318 href: query
1319 });
1320
1321 dialog.show();
1322 }
1323
1324 function insertSSLserial(value) {
1325 dijit.byId("SSL_CERT_SERIAL").attr('value', value);
1326 }
1327
1328 function gotoExportOpml(filename, settings) {
1329 const tmp = settings ? 1 : 0;
1330 document.location.href = "backend.php?op=opml&method=export&filename=" + filename + "&settings=" + tmp;
1331 }
1332
1333
1334 function batchSubscribe() {
1335 const query = "backend.php?op=pref-feeds&method=batchSubscribe";
1336
1337 // overlapping widgets
1338 if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
1339 if (dijit.byId("feedAddDlg")) dijit.byId("feedAddDlg").destroyRecursive();
1340
1341 const dialog = new dijit.Dialog({
1342 id: "batchSubDlg",
1343 title: __("Batch subscribe"),
1344 style: "width: 600px",
1345 execute: function () {
1346 if (this.validate()) {
1347 notify_progress(__("Subscribing to feeds..."), true);
1348
1349 xhrPost("backend.php", this.attr('value'), () => {
1350 notify("");
1351 updateFeedList();
1352 dialog.hide();
1353 });
1354 }
1355 },
1356 href: query
1357 });
1358
1359 dialog.show();
1360 }
1361
1362 function clearPluginData(name) {
1363 if (confirm(__("Clear stored data for this plugin?"))) {
1364 notify_progress("Loading, please wait...");
1365
1366 xhrPost("backend.php", { op: "pref-prefs", method: "clearplugindata", name: name }, () => {
1367 notify('');
1368 updatePrefsList();
1369 });
1370 }
1371 }
1372
1373 function clearSqlLog() {
1374
1375 if (confirm(__("Clear all messages in the error log?"))) {
1376
1377 notify_progress("Loading, please wait...");
1378
1379 xhrPost("backend.php", { op: "pref-system", method: "clearLog" }, () => {
1380 updateSystemList();
1381 });
1382
1383 }
1384 }
1385
1386 function updateSelectedPrompt() {
1387 // no-op shim for toggleSelectedRow()
1388 }
1389