Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: chrome/browser/resources/policy.js

Issue 12084065: Convert chrome://policy to new WebUI style (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/resources/policy.html ('k') | chrome/browser/ui/webui/policy_ui.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 cr.define('policy', function() {
6 * This variable structure is here to document the structure that the template 6 /**
7 * expects to correctly populate the page. 7 * A box that shows the status of cloud policy for a device or user.
8 */ 8 * @constructor
9 var policyDataFormat = { 9 * @extends {HTMLFieldSetElement}
10 // Whether any of the policies in 'policies' have a value. 10 */
11 'anyPoliciesSet': true, 11 var StatusBox = cr.ui.define(function() {
12 12 var node = $('status-box-template').cloneNode(true);
13 'policies': [ 13 node.removeAttribute('id');
14 { 14 return node;
15 'level': 'managed', 15 });
16 'name': 'AllowXYZ', 16
17 'set': true, 17 StatusBox.prototype = {
18 'scope': 'Machine', 18 // Set up the prototype chain.
19 'status': 'ok', 19 __proto__: HTMLFieldSetElement.prototype,
20 'value': true 20
21 } 21 /**
22 ], 22 * Initialization function for the cr.ui framework.
23 'status': { 23 */
24 'deviceFetchInterval': '8min', 24 decorate: function() {
25 'deviceId': 'D2AC39A2-3C8FC-E2C0-E45D2DC3782C', 25 },
26 'deviceLastFetchTime': '9:50 PM', 26
27 'devicePolicyDomain': 'google.com', 27 /**
28 'deviceStatusMessage': 'OK', 28 * Populate the box with the given cloud policy status.
29 'displayDeviceStatus': true, 29 * @param {string} scope The policy scope, either "device" or "user".
30 'displayStatusSection': true, 30 * @param {Object} status Dictionary with information about the status.
31 'displayUserStatus': true, 31 */
32 'user': 'simo@google.com', 32 initialize: function(scope, status) {
33 'userFetchInterval': '8min', 33 if (scope == 'device') {
34 'userId': 'D2AC39A2-3C8FC-E2C0-E45D2DC3782C', 34 // For device policy, set the appropriate title and populate the topmost
35 'userLastFetchTime': '9:50 PM', 35 // status item with the domain the device is enrolled into.
36 'userStatusMessage': 'OK' 36 this.querySelector('.legend').textContent =
37 loadTimeData.getString('statusDevice');
38 var domain = this.querySelector('.domain');
39 domain.textContent = status.domain;
40 domain.parentElement.hidden = false;
41 } else {
42 // For user policy, set the appropriate title and populate the topmost
43 // status item with the username that policies apply to.
44 this.querySelector('.legend').textContent =
45 loadTimeData.getString('statusUser');
46 // Populate the topmost item with the username.
47 var username = this.querySelector('.username');
48 username.textContent = status.username;
49 username.parentElement.hidden = false;
50 }
51 // Populate all remaining items.
52 this.querySelector('.client-id').textContent = status.clientId || '';
53 this.querySelector('.time-since-last-refresh').textContent =
54 status.timeSinceLastRefresh || '';
55 this.querySelector('.refresh-interval').textContent =
56 status.refreshInterval || '';
57 this.querySelector('.status').textContent = status.status || '';
58 },
59 };
60
61 /**
62 * A single policy's entry in the policy table.
63 * @constructor
64 * @extends {HTMLTableSectionElement}
65 */
66 var Policy = cr.ui.define(function() {
67 var node = $('policy-template').cloneNode(true);
68 node.removeAttribute('id');
69 return node;
70 });
71
72 Policy.prototype = {
73 // Set up the prototype chain.
74 __proto__: HTMLTableSectionElement.prototype,
75
76 /**
77 * Initialization function for the cr.ui framework.
78 */
79 decorate: function() {
80 this.updateToggleExpandedValueText_();
81 this.querySelector('.toggle-expanded-value').addEventListener(
82 'click', this.toggleExpandedValue_.bind(this));
83 },
84
85 /**
86 * Populate the table columns with information about the policy name, value
87 * and status.
88 * @param {string} name The policy name.
89 * @param {Object} value Dictionary with information about the policy value.
90 * @param {boolean} unknown Whether the policy name is not recognized.
91 */
92 initialize: function(name, value, unknown) {
93 this.name = name;
94 this.unset = !value;
95
96 // Populate the name column.
97 this.querySelector('.name').textContent = name;
98
99 // Populate the remaining columns with policy scope, level and value if a
100 // value has been set. Otherwise, leave them blank.
101 if (value) {
102 this.querySelector('.scope').textContent =
103 loadTimeData.getString(value.scope == 'user' ?
104 'scopeUser' : 'scopeDevice');
105 this.querySelector('.level').textContent =
106 loadTimeData.getString(value.level == 'recommended' ?
107 'levelRecommended' : 'levelMandatory');
108 this.querySelector('.value').textContent = value.value;
109 this.querySelector('.expanded-value').textContent = value.value;
110 }
111
112 // Populate the status column.
113 var status;
114 if (!value) {
115 // If the policy value has not been set, show an error message.
116 status = loadTimeData.getString('unset');
117 } else if (unknown) {
118 // If the policy name is not recognized, show an error message.
119 status = loadTimeData.getString('unknown');
120 } else if (value.error) {
121 // If an error occurred while parsing the policy value, show the error
122 // message.
123 status = value.error;
124 } else {
125 // Otherwise, indicate that the policy value was parsed correctly.
126 status = loadTimeData.getString('ok');
127 }
128 this.querySelector('.status').textContent = status;
129 },
130
131 /**
132 * Check the table columns for overflow. Most columns are automatically
133 * elided when overflow occurs. The only action required is to add a tooltip
134 * that shows the complete content. The value column is an exception. If
135 * overflow occurs here, the contents is replaced with a link that toggles
136 * the visibility of an additional row containing the complete value.
137 */
138 checkOverflow: function() {
139 // Set a tooltip on all overflowed columns except the value column.
140 var divs = this.querySelectorAll('div.elide');
141 for (var i = 0; i < divs.length; i++) {
142 var div = divs[i];
143 div.title = div.offsetWidth < div.scrollWidth ? div.textContent : '';
144 }
145
146 // Cache the width of the value column's contents when it is first shown.
147 // This is required to be able to check whether the contents would still
148 // overflow the column once it has been hidden and replaced by a link.
149 var valueContainer = this.querySelector('.value-container');
150 if (valueContainer.valueWidth == undefined) {
151 valueContainer.valueWidth =
152 valueContainer.querySelector('.value').offsetWidth;
153 }
154
155 // Determine whether the contents of the value column overflows. The
156 // visibility of the contents, replacement link and additional row
157 // containing the complete value that depend on this are handled by CSS.
158 this.classList.toggle(
159 'has-overflowed-value',
160 valueContainer.offsetWidth < valueContainer.valueWidth);
161 },
162
163 /**
164 * Update the text of the link that toggles the visibility of an additional
165 * row containing the complete policy value, depending on the toggle state.
166 * @private
167 */
168 updateToggleExpandedValueText_: function(event) {
169 this.querySelector('.toggle-expanded-value').textContent =
170 loadTimeData.getString(
171 this.classList.contains('show-overflowed-value') ?
172 'hideExpandedValue' : 'showExpandedValue');
173 },
174
175 /**
176 * Toggle the visibility of an additional row containing the complete policy
177 * value.
178 * @private
179 */
180 toggleExpandedValue_: function() {
181 this.classList.toggle('show-overflowed-value');
182 this.updateToggleExpandedValueText_();
183 },
184 };
185
186 /**
187 * A table of policies and their values.
188 * @constructor
189 * @extends {HTMLTableSectionElement}
190 */
191 var PolicyTable = cr.ui.define('tbody');
192
193 PolicyTable.prototype = {
194 // Set up the prototype chain.
195 __proto__: HTMLTableSectionElement.prototype,
196
197 /**
198 * Initialization function for the cr.ui framework.
199 */
200 decorate: function() {
201 this.policies_ = {};
202 this.filterPattern_ = '';
203 window.addEventListener('resize', this.checkOverflow_.bind(this));
204 },
205
206 /**
207 * Initialize the list of all known policies.
208 * @param {Object} names Dictionary containing all known policy names.
209 */
210 setPolicyNames: function(names) {
211 this.policies_ = names;
212 this.setPolicyValues({});
213 },
214
215 /**
216 * Populate the table with the currently set policy values and any errors
217 * detected while parsing these.
218 * @param {Object} values Dictionary containing the current policy values.
219 */
220 setPolicyValues: function(values) {
221 // Remove all policies from the table.
222 var policies = this.getElementsByTagName('tbody');
223 while (policies.length > 0)
224 this.removeChild(policies.item(0));
225
226 // First, add known policies whose value is currently set.
227 var unset = [];
228 for (var name in this.policies_) {
229 if (name in values)
230 this.setPolicyValue_(name, values[name], false);
231 else
232 unset.push(name);
233 }
234
235 // Second, add policies whose value is currently set but whose name is not
236 // recognized.
237 for (var name in values) {
238 if (!(name in this.policies_))
239 this.setPolicyValue_(name, values[name], true);
240 }
241
242 // Finally, add known policies whose value is not currently set.
243 for (var i = 0; i < unset.length; i++)
244 this.setPolicyValue_(unset[i], undefined, false);
245
246 // Filter the policies.
247 this.filter();
248 },
249
250 /**
251 * Set the filter pattern. Only policies whose name contains |pattern| are
252 * shown in the policy table. The filter is case insensitive. It can be
253 * disabled by setting |pattern| to an empty string.
254 * @param {string} pattern The filter pattern.
255 */
256 setFilterPattern: function(pattern) {
257 this.filterPattern_ = pattern.toLowerCase();
258 this.filter();
259 },
260
261 /**
262 * Filter policies. Only policies whose name contains the filter pattern are
263 * shown in the table. Furthermore, policies whose value is not currently
264 * set are only shown if the corresponding checkbox is checked.
265 */
266 filter: function() {
267 var showUnset = $('show-unset').checked;
268 var policies = this.getElementsByTagName('tbody');
269 for (var i = 0; i < policies.length; i++) {
270 var policy = policies[i];
271 policy.hidden =
272 policy.unset && !showUnset ||
273 policy.name.toLowerCase().indexOf(this.filterPattern_) == -1;
274 }
275 this.parentElement.classList.toggle(
276 'empty', !this.querySelector('tbody:not([hidden])'));
277 setTimeout(this.checkOverflow_.bind(this), 0);
278 },
279
280 /**
281 * Check the table columns for overflow.
282 * @private
283 */
284 checkOverflow_: function() {
285 var policies = this.getElementsByTagName('tbody');
286 for (var i = 0; i < policies.length; i++) {
287 if (!policies[i].hidden)
288 policies[i].checkOverflow();
289 }
290 },
291
292 /**
293 * Add a policy with the given |name| and |value| to the table.
294 * @param {string} name The policy name.
295 * @param {Object} value Dictionary with information about the policy value.
296 * @param {boolean} unknown Whether the policy name is not recoginzed.
297 * @private
298 */
299 setPolicyValue_: function(name, value, unknown) {
300 var policy = new Policy;
301 policy.initialize(name, value, unknown);
302 this.appendChild(policy);
303 },
304 };
305
306 /**
307 * A singelton object that handles communication between browser and WebUI.
308 * @constructor
309 */
310 function Page() {
37 } 311 }
38 }; 312
39 313 // Make Page a singleton.
40 cr.define('policies', function() { 314 cr.addSingletonGetter(Page);
41 315
42 function Policy() { 316 /**
43 } 317 * Provide a list of all known policies to the UI. Called by the browser on
44 318 * page load.
45 cr.addSingletonGetter(Policy); 319 * @param {Object} names Dictionary containing all known policy names.
46 320 */
47 Policy.prototype = { 321 Page.setPolicyNames = function(names) {
48 /** 322 this.getInstance().policyTable.setPolicyNames(names);
49 * True if none of the received policies are actually set, false otherwise. 323 };
50 * @type {boolean} 324
51 */ 325 /**
52 noActivePolicies_: false, 326 * Provide a list of the currently set policy values and any errors detected
53 327 * while parsing these to the UI. Called by the browser on page load and
54 /** 328 * whenever policy values change.
55 * The current search term for filtering of the policy table. 329 * @param {Object} values Dictionary containing the current policy values.
56 * @type {string} 330 */
57 * @private 331 Page.setPolicyValues = function(values) {
58 */ 332 this.getInstance().policyTable.setPolicyValues(values);
59 searchTerm_: '', 333 };
60 334
61 /** 335 /**
62 * Takes the |policyData| argument and populates the page with this data. It 336 * Provide the current cloud policy status to the UI. Called by the browser on
63 * expects an object structure like the policyDataFormat above. 337 * page load if cloud policy is present and whenever the status changes.
64 * @param {Object} policyData Detailed info about policies. 338 * @param {Object} status Dictionary containing the current policy status.
65 */ 339 */
66 renderTemplate: function(policyData) { 340 Page.setStatus = function(status) {
67 this.noActivePolicies_ = !policyData.anyPoliciesSet; 341 this.getInstance().setStatus(status);
68 342 };
69 if (this.noActivePolicies_) 343
70 $('no-policies').hidden = false; 344 /**
71 if (policyData.status.displayStatusSection) 345 * Notify the UI that a request to reload policy values has completed. Called
72 $('status-section').hidden = false; 346 * by the browser after a request to reload policy has been sent by the UI.
73 347 */
74 // This is the javascript code that processes the template: 348 Page.reloadPoliciesDone = function() {
75 var input = new JsEvalContext(policyData); 349 this.getInstance().reloadPoliciesDone();
76 var output = $('data-template'); 350 };
77 jstProcess(input, output); 351
78 352 Page.prototype = {
79 var toggles = document.querySelectorAll('.policy-set * .toggler'); 353 /**
80 for (var i = 0; i < toggles.length; i++) { 354 * Main initialization function. Called by the browser on page load.
81 toggles[i].hidden = true; 355 */
82 toggles[i].onclick = function() { 356 initialize: function() {
83 Policy.getInstance().toggleCellExpand_(this); 357 uber.onContentFrameLoaded();
84 }; 358 this.policyTable = $('policy-table');
85 } 359 cr.ui.decorate(this.policyTable, PolicyTable);
86 360
87 var containers = document.querySelectorAll('.text-container'); 361 // Place the initial focus on the filter input field.
88 for (var i = 0; i < containers.length; i++) 362 $('filter').focus();
89 this.initTextContainer_(containers[i]); 363
90 }, 364 var self = this;
91 365 $('filter').onsearch = function(event) {
92 /** 366 self.policyTable.setFilterPattern(this.value);
93 * Filters the table of policies by name. 367 };
94 * @param {string} term The search string. 368 $('reload-policies').onclick = function(event) {
95 */ 369 this.disabled = true;
96 filterTable: function(term) { 370 chrome.send('reloadPolicies');
97 this.searchTerm_ = term.toLowerCase(); 371 };
98 var table = $('policy-table'); 372 $('show-unset').onchange = this.policyTable.filter.bind(this.policyTable);
99 var showUnsent = $('toggle-unsent-policies').checked; 373
100 for (var r = 1; r < table.rows.length; r++) { 374 // Notify the browser that the page has loaded, causing it to send the
101 var row = table.rows[r]; 375 // list of all known policies, the current policy values and the cloud
102 376 // policy status.
103 // Don't change visibility of policies that aren't set if the checkbox 377 chrome.send('initialized');
104 // isn't checked. 378 },
105 if (!showUnsent && row.className == 'policy-unset') 379
106 continue; 380 /**
107 381 * Update the status section of the page to show the current cloud policy
108 var nameCell = row.querySelector('.policy-name'); 382 * status.
109 var cellContents = nameCell.textContent; 383 * @param {Object} status Dictionary containing the current policy status.
110 row.hidden = 384 */
111 !(cellContents.toLowerCase().indexOf(this.searchTerm_) >= 0); 385 setStatus: function(status) {
112 } 386 // Remove any existing status boxes.
113 }, 387 var container = $('status-box-container');
114 388 while (container.firstChild)
115 /** 389 container.removeChild(container.firstChild);
116 * Updates the visibility of the policies depending on the state of the 390 // Hide the status section.
117 * 'toggle-unsent-policies' checkbox. 391 var section = $('status');
118 */ 392 section.hidden = true;
119 updatePolicyVisibility: function() { 393
120 if ($('toggle-unsent-policies').checked) 394 // Add a status box for each scope that has a cloud policy status.
121 $('policies').style.display = ''; 395 for (var scope in status) {
122 else if (this.noActivePolicies_) 396 var box = new StatusBox;
123 $('policies').style.display = 'none'; 397 box.initialize(scope, status[scope]);
124 398 container.appendChild(box);
125 var tableRows = document.getElementsByClassName('policy-unset'); 399 // Show the status section.
126 for (var i = 0; i < tableRows.length; i++) 400 section.hidden = false;
127 tableRows[i].hidden = !($('toggle-unsent-policies').checked); 401 }
128 402 },
129 // Filter table again in case a search was active. 403
130 this.filterTable(this.searchTerm_); 404 /**
131 }, 405 * Re-enable the reload policies button when the previous request to reload
132 406 * policies values has completed.
133 /** 407 */
134 * Expands or collapses a table cell that has overflowing text. 408 reloadPoliciesDone: function() {
135 * @param {Object} toggler The toggler that was clicked on. 409 $('reload-policies').disabled = false;
136 * @private 410 },
137 */ 411 };
138 toggleCellExpand_: function(toggler) { 412
139 var textContainer = toggler.parentElement;
140 textContainer.collapsed = !textContainer.collapsed;
141
142 if (textContainer.collapsed)
143 this.collapseCell_(textContainer);
144 else
145 this.expandCell_(textContainer);
146 },
147
148 /**
149 * Collapses all expanded table cells and updates the visibility of the
150 * toggles accordingly. Should be called before the policy information in
151 * the table is updated.
152 */
153 collapseExpandedCells: function() {
154 var textContainers = document.querySelectorAll('.text-expanded');
155 for (var i = 0; i < textContainers.length; i++)
156 this.collapseCell_(textContainers[i]);
157 },
158
159 /**
160 * Expands a table cell so that all the text it contains is visible.
161 * @param {Object} textContainer The cell's div element that contains the
162 * text.
163 * @private
164 */
165 expandCell_: function(textContainer) {
166 textContainer.classList.remove('text-collapsed');
167 textContainer.classList.add('text-expanded');
168 textContainer.querySelector('.expand').hidden = true;
169 textContainer.querySelector('.collapse').hidden = false;
170 },
171
172 /**
173 * Collapses a table cell so that overflowing text is hidden.
174 * @param {Object} textContainer The cell's div element that contains the
175 * text.
176 * @private
177 */
178 collapseCell_: function(textContainer) {
179 textContainer.classList.remove('text-expanded');
180 textContainer.classList.add('text-collapsed');
181 textContainer.querySelector('.expand').hidden = false;
182 textContainer.querySelector('.collapse').hidden = true;
183 },
184
185 /**
186 * Initializes a text container, showing the expand toggle if necessary.
187 * @param {Object} textContainer The text container element.
188 */
189 initTextContainer_: function(textContainer) {
190 textContainer.collapsed = true;
191 var textValue = textContainer.querySelector('.text-value');
192
193 // If the text is wider than the text container, the expand toggler should
194 // appear.
195 if (textContainer.offsetWidth < textValue.offsetWidth ||
196 textContainer.offsetHeight < textValue.offsetHeight) {
197 this.collapseCell_(textContainer);
198 }
199 }
200 };
201
202 /**
203 * Asks the C++ PolicyUIHandler to get details about policies and status
204 * information. The PolicyUIHandler should reply to returnData() (below).
205 */
206 Policy.requestData = function() {
207 chrome.send('requestData');
208 };
209
210 /**
211 * Called by the C++ PolicyUIHandler when it has the requested data.
212 * @param {Object} policyData The policy information in the format described
213 * by the policyDataFormat.
214 */
215 Policy.returnData = function(policyData) {
216 var policy = Policy.getInstance();
217 policy.collapseExpandedCells();
218 policy.renderTemplate(policyData);
219 policy.updatePolicyVisibility();
220 };
221
222 /**
223 * Called by the C++ PolicyUIHandler when a requested policy refresh has
224 * completed.
225 */
226 Policy.refreshDone = function() {
227 $('fetch-policies-button').disabled = false;
228 };
229
230 /**
231 * Asks the C++ PolicyUIHandler to re-fetch policy information.
232 */
233 Policy.triggerPolicyFetch = function() {
234 chrome.send('fetchPolicy');
235 };
236
237 /**
238 * Determines whether a policy should be visible or not.
239 * @param {Object} policy An entry in the 'policies' array given by the above
240 * PolicyDataFormat.
241 */
242 Policy.shouldDisplayPolicy = function(policy) {
243 return $('toggle-unsent-policies').checked || policy.set;
244 };
245
246 /**
247 * Initializes the page and loads the list of policies and the policy
248 * status data.
249 */
250 Policy.initialize = function() {
251 Policy.requestData();
252
253 // Set HTML event handlers.
254 $('fetch-policies-button').onclick = function(event) {
255 this.disabled = true;
256 Policy.triggerPolicyFetch();
257 };
258
259 $('toggle-unsent-policies').onchange = function(event) {
260 Policy.getInstance().updatePolicyVisibility();
261 };
262
263 $('search-field').onsearch = function(event) {
264 Policy.getInstance().filterTable(this.value);
265 };
266 };
267
268 // Export
269 return { 413 return {
270 Policy: Policy 414 Page: Page
271 }; 415 };
272 }); 416 });
273 417
274 var Policy = policies.Policy; 418 // Have the main initialization function be called when the page finishes
275 419 // loading.
276 // Get data and have it displayed upon loading. 420 document.addEventListener(
277 document.addEventListener('DOMContentLoaded', policies.Policy.initialize); 421 'DOMContentLoaded',
422 policy.Page.getInstance().initialize.bind(policy.Page.getInstance()));
OLDNEW
« no previous file with comments | « chrome/browser/resources/policy.html ('k') | chrome/browser/ui/webui/policy_ui.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698