treewide: always include cbi.js
[project/luci.git] / modules / luci-base / luasrc / view / cbi / apply_widget.htm
1 <% export("cbi_apply_widget", function(redirect_ok) -%>
2 <style type="text/css">
3 .alert-message.notice {
4 background: linear-gradient(#fff 0%, #eee 100%);
5 }
6
7 #cbi_apply_overlay {
8 position: absolute;
9 top: 0;
10 left: 0;
11 bottom: 0;
12 right: 0;
13 background: rgba(0, 0, 0, 0.7);
14 display: none;
15 z-index: 20000;
16 }
17
18 #cbi_apply_overlay .alert-message {
19 position: relative;
20 top: 10%;
21 width: 60%;
22 margin: auto;
23 display: flex;
24 flex-wrap: wrap;
25 min-height: 32px;
26 align-items: center;
27 }
28
29 #cbi_apply_overlay .alert-message > h4,
30 #cbi_apply_overlay .alert-message > p,
31 #cbi_apply_overlay .alert-message > div {
32 flex-basis: 100%;
33 }
34
35 #cbi_apply_overlay .alert-message > img {
36 margin-right: 1em;
37 flex-basis: 32px;
38 }
39
40 body.apply-overlay-active {
41 overflow: hidden;
42 height: 100vh;
43 }
44
45 body.apply-overlay-active #cbi_apply_overlay {
46 display: block;
47 }
48 </style>
49
50 <script type="text/javascript">//<![CDATA[
51 var xhr = new XHR(),
52 uci_apply_auth = { sid: '<%=luci.dispatcher.context.authsession%>', token: '<%=token%>' },
53 uci_apply_rollback = <%=math.max(luci.config and luci.config.apply and luci.config.apply.rollback or 30, 30)%>,
54 uci_apply_holdoff = <%=math.max(luci.config and luci.config.apply and luci.config.apply.holdoff or 4, 1)%>,
55 uci_apply_timeout = <%=math.max(luci.config and luci.config.apply and luci.config.apply.timeout or 5, 1)%>,
56 uci_apply_display = <%=math.max(luci.config and luci.config.apply and luci.config.apply.display or 1.5, 1)%>;
57
58 function uci_status_message(type, content) {
59 var overlay = document.getElementById('cbi_apply_overlay') || document.body.appendChild(E('<div id="cbi_apply_overlay"><div class="alert-message"></div></div>')),
60 message = overlay.querySelector('.alert-message');
61
62 if (message && type) {
63 if (!message.classList.contains(type)) {
64 message.classList.remove('notice');
65 message.classList.remove('warning');
66 message.classList.add(type);
67 }
68
69 if (content)
70 message.innerHTML = content;
71
72 document.body.classList.add('apply-overlay-active');
73 }
74 else {
75 document.body.classList.remove('apply-overlay-active');
76 }
77 }
78
79 function uci_rollback(checked) {
80 if (checked) {
81 uci_status_message('warning',
82 '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
83 '<%:Failed to confirm apply within %ds, waiting for rollback…%>'.format(uci_apply_rollback));
84
85 var call = function(r) {
86 if (r.status === 204) {
87 uci_status_message('warning',
88 '<h4><%:Configuration has been rolled back!%></h4>' +
89 '<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) +
90 '<div class="right">' +
91 '<input type="button" class="btn" onclick="uci_status_message(false)" value="<%:Dismiss%>" /> ' +
92 '<input type="button" class="btn" onclick="uci_revert()" value="<%:Revert changes%>" /> ' +
93 '<input type="button" class="btn danger" onclick="uci_apply(false)" value="<%:Apply unchecked%>" />' +
94 '</div>');
95
96 return;
97 }
98
99 xhr.post('<%=url("admin/uci/confirm")%>', uci_apply_auth, call, uci_apply_timeout * 1000);
100 };
101
102 call({ status: 0 });
103 }
104 else {
105 uci_status_message('warning',
106 '<h4><%:Device unreachable!%></h4>' +
107 '<p><%:Could not regain access to the device after applying the configuration changes. You might need to reconnect if you modified network related settings such as the IP address or wireless security credentials.%></p>');
108 }
109 }
110
111 function uci_confirm(checked, deadline) {
112 var tt;
113 var ts = Date.now();
114
115 uci_status_message('notice');
116
117 var call = function(r) {
118 if (Date.now() >= deadline) {
119 uci_rollback(checked);
120 return;
121 }
122 else if (r && (r.status === 200 || r.status === 204)) {
123 var indicator = document.querySelector('.uci_change_indicator');
124 if (indicator) indicator.style.display = 'none';
125
126 uci_status_message('notice', '<%:Configuration has been applied.%>');
127
128 window.clearTimeout(tt);
129 window.setTimeout(function() {
130 <% if redirect_ok then -%>
131 location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
132 <%- else -%>
133 window.location = window.location.href.split('#')[0];
134 <% end %>
135 }, uci_apply_display * 1000);
136
137 return;
138 }
139
140 xhr.post('<%=url("admin/uci/confirm")%>', uci_apply_auth, call, uci_apply_timeout * 1000);
141 };
142
143 var tick = function() {
144 var now = Date.now();
145
146 uci_status_message('notice',
147 '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
148 '<%:Waiting for configuration to get applied… %ds%>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0)));
149
150 if (now >= deadline)
151 return;
152
153 tt = window.setTimeout(tick, 1000 - (now - ts));
154 ts = now;
155 };
156
157 tick();
158
159 /* wait a few seconds for the settings to become effective */
160 window.setTimeout(call, Math.max(uci_apply_holdoff * 1000 - ((ts + uci_apply_rollback * 1000) - deadline), 1));
161 }
162
163 function uci_apply(checked) {
164 uci_status_message('notice',
165 '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
166 '<%:Starting configuration apply…%>');
167
168 xhr.post('<%=url("admin/uci")%>/' + (checked ? 'apply_rollback' : 'apply_unchecked'), uci_apply_auth, function(r) {
169 if (r.status === (checked ? 200 : 204)) {
170 uci_confirm(checked, Date.now() + uci_apply_rollback * 1000);
171 }
172 else if (checked && r.status === 204) {
173 uci_status_message('notice', '<%:There are no changes to apply.%>');
174 window.setTimeout(function() {
175 <% if redirect_ok then -%>
176 location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
177 <%- else -%>
178 uci_status_message(false);
179 <%- end %>
180 }, uci_apply_display * 1000);
181 }
182 else {
183 uci_status_message('warning', '<%_Apply request failed with status <code>%h</code>%>'.format(r.responseText || r.statusText || r.status));
184 window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
185 }
186 });
187 }
188
189 function uci_revert() {
190 uci_status_message('notice',
191 '<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
192 '<%:Reverting configuration…%>');
193
194 xhr.post('<%=url("admin/uci/revert")%>', uci_apply_auth, function(r) {
195 if (r.status === 200) {
196 uci_status_message('notice', '<%:Changes have been reverted.%>');
197 window.setTimeout(function() {
198 <% if redirect_ok then -%>
199 location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
200 <%- else -%>
201 window.location = window.location.href.split('#')[0];
202 <%- end %>
203 }, uci_apply_display * 1000);
204 }
205 else {
206 uci_status_message('warning', '<%_Revert request failed with status <code>%h</code>%>'.format(r.statusText || r.status));
207 window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
208 }
209 });
210 }
211 //]]></script>
212 <%- end) %>