blob: 3ffd2982b0601679dfcadeefc5ffd3da202b33dc [file] [log] [blame]
avm99963f5923962020-12-07 16:44:37 +01001function parseUrl(url) {
2 var forum_a = url.match(/forum\/([0-9]+)/i);
3 var thread_a = url.match(/thread\/([0-9]+)/i);
4
5 if (forum_a === null || thread_a === null) {
6 return false;
7 }
8
9 return {
10 'forum': forum_a[1],
11 'thread': thread_a[1],
12 };
13}
14
15function recursiveParentElement(el, tag) {
16 while (el !== document.documentElement) {
17 el = el.parentNode;
18 if (el.tagName == tag) return el;
19 }
20 return undefined;
21}
22
avm999638d3b4ca2020-12-08 13:21:52 +010023function returnAuthUser() {
24 var startup =
25 JSON.parse(document.querySelector('html').getAttribute('data-startup'));
26 return startup[2][1] || '0';
27}
28
avm99963f5923962020-12-07 16:44:37 +010029// Source:
30// https://stackoverflow.com/questions/33063774/communication-from-an-injected-script-to-the-content-script-with-a-response
31var contentScriptRequest = (function() {
32 var requestId = 0;
33 var prefix = 'TWPT-batchlock-generic';
34
35 function sendRequest(data) {
36 var id = requestId++;
37
38 return new Promise(function(resolve, reject) {
39 var listener = function(evt) {
40 if (evt.source === window && evt.data && evt.data.prefix === prefix &&
41 evt.data.requestId == id) {
42 // Deregister self
43 window.removeEventListener('message', listener);
44 resolve(evt.data.data);
45 }
46 };
47
48 window.addEventListener('message', listener);
49
50 var payload = {data, id, prefix};
51
52 window.dispatchEvent(
53 new CustomEvent('TWPT_sendRequest', {detail: payload}));
54 });
55 }
56
57 return {sendRequest: sendRequest};
58})();
59
60function enableEndButtons() {
61 var buttons = document.querySelectorAll(
62 '.pane[pane-id="default-1"] footer[data-footer-id="1"] material-button');
63 buttons.forEach(btn => {
64 btn.classList.remove('is-disabled');
65 });
66}
67
68function addLogEntry(success, action, url, threadId, errDetails = null) {
69 var p1 = contentScriptRequest.sendRequest({
70 action: 'geti18nMessage',
71 msg: 'inject_lockdialog_log_entry_beginning',
72 placeholders: [threadId],
73 });
74
75 var p2 = contentScriptRequest.sendRequest({
76 action: 'geti18nMessage',
77 msg: 'inject_lockdialog_log_entry_' + (success ? 'success' : 'error') +
78 '_' + action,
79 placeholders: [errDetails],
80 });
81
82 Promise.all([p1, p2]).then(strings => {
83 var log = document.getElementById('TWPT-lock-log');
84 var logEntry = document.createElement('p');
85 logEntry.classList.add(
86 'TWPT-log-entry', 'TWPT-log-entry--' + (success ? 'success' : 'error'));
87
88 var a = document.createElement('a');
89 a.href = url;
90 a.target = '_blank';
91 a.textContent = strings[0];
92
93 var end = document.createTextNode(': ' + strings[1]);
94
95 logEntry.append(a, end);
96 log.append(logEntry);
97 });
98}
99
100function lockThreads(action) {
101 var modal = document.querySelector('.pane[pane-id="default-1"]');
102 modal.querySelector('footer[data-footer-id="0"]').classList.add('is-hidden');
103 modal.querySelector('footer[data-footer-id="1"]')
104 .classList.remove('is-hidden');
105
106 var checkboxes = document.querySelectorAll(
107 '.thread-group material-checkbox[aria-checked="true"]');
108
109 var p = document.createElement('p');
110 p.style.textAlign = 'center';
111
112 var progress = document.createElement('progress');
113 progress.max = checkboxes.length;
114 progress.value = 0;
115
116 p.append(progress);
117
118 var log = document.createElement('div');
119 log.id = 'TWPT-lock-log';
120 log.classList.add('TWPT-log');
121
122 modal.querySelector('main').textContent = '';
123 modal.querySelector('main').append(p, log);
124
avm999638d3b4ca2020-12-08 13:21:52 +0100125 var authuser = returnAuthUser();
126 var APIRequestUrl =
127 'https://support.google.com/s/community/api/SetThreadAttribute' +
128 (authuser == '0' ? '' : '?authuser=' + encodeURIComponent(authuser));
129
avm99963f5923962020-12-07 16:44:37 +0100130 checkboxes.forEach(checkbox => {
131 var url = recursiveParentElement(checkbox, 'A').href;
132 var thread = parseUrl(url);
133 if (thread === false) {
134 console.error('Fatal error: thread URL ' + url + ' could not be parsed.');
135 return;
136 }
avm999638d3b4ca2020-12-08 13:21:52 +0100137 fetch(APIRequestUrl, {
avm99963f5923962020-12-07 16:44:37 +0100138 'headers': {
139 'content-type': 'text/plain; charset=utf-8',
140 },
141 'body': JSON.stringify({
142 1: thread.forum,
143 2: thread.thread,
144 3: (action == 'lock' ? 1 : 2),
145 }),
146 'method': 'POST',
147 'mode': 'cors',
148 'credentials': 'include',
149 })
150 .then(res => {
151 if (res.status == 200 || res.status == 400) {
152 return res.json().then(data => ({
153 status: res.status,
154 body: data,
155 }));
156 } else {
157 throw new Error('Status code ' + res.status + ' was not expected.');
158 }
159 })
160 .then(res => {
161 if (res.status == 400) {
162 throw new Error(
163 res.body[4] ||
164 ('Response status: 400. Error code: ' + res.body[2]));
165 }
166 })
167 .then(_ => {
168 addLogEntry(true, action, url, thread.thread);
169 })
170 .catch(err => {
171 console.error(
172 'An error occurred while locking thread ' + url + ': ' + err);
173 addLogEntry(false, action, url, thread.thread, err);
174 })
175 .then(_ => {
176 progress.value = parseInt(progress.value) + 1;
177 if (progress.value == progress.getAttribute('max'))
178 enableEndButtons();
179 });
180 });
181}
182
183window.addEventListener('message', e => {
184 if (e.source === window && e.data && e.data.prefix === 'TWPT-batchlock' &&
185 e.data.action) {
186 switch (e.data.action) {
187 case 'lock':
188 case 'unlock':
189 console.info('Performing action ' + e.data.action);
190 lockThreads(e.data.action);
191 break;
192
193 default:
194 console.error(
195 'Action \'' + e.data.action +
196 '\' unknown to TWPT-batchlock receiver.');
197 }
198 }
199});