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

Side by Side Diff: chrome/browser/ui/webui/policy_ui.cc

Issue 12084065: Convert chrome://policy to new WebUI style (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Move string to the correct position. 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 #include "chrome/browser/ui/webui/policy_ui.h" 5 #include "chrome/browser/ui/webui/policy_ui.h"
6 6
7 #include <vector>
8
9 #include "base/bind.h" 7 #include "base/bind.h"
10 #include "base/bind_helpers.h" 8 #include "base/bind_helpers.h"
11 #include "base/callback.h" 9 #include "base/callback.h"
12 #include "base/compiler_specific.h" 10 #include "base/logging.h"
13 #include "base/hash_tables.h" 11 #include "base/string16.h"
14 #include "base/stl_util.h"
15 #include "base/time.h" 12 #include "base/time.h"
16 #include "base/utf_string_conversions.h" 13 #include "base/values.h"
17 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/policy/browser_policy_connector.h" 15 #include "chrome/browser/policy/browser_policy_connector.h"
19 #include "chrome/browser/policy/cloud_policy_client.h" 16 #include "chrome/browser/policy/cloud_policy_client.h"
17 #include "chrome/browser/policy/cloud_policy_constants.h"
20 #include "chrome/browser/policy/cloud_policy_core.h" 18 #include "chrome/browser/policy/cloud_policy_core.h"
21 #include "chrome/browser/policy/cloud_policy_refresh_scheduler.h" 19 #include "chrome/browser/policy/cloud_policy_refresh_scheduler.h"
22 #include "chrome/browser/policy/cloud_policy_store.h" 20 #include "chrome/browser/policy/cloud_policy_store.h"
23 #include "chrome/browser/policy/cloud_policy_validator.h" 21 #include "chrome/browser/policy/cloud_policy_validator.h"
22 #include "chrome/browser/policy/configuration_policy_handler_list.h"
23 #include "chrome/browser/policy/device_local_account_policy_service.h"
24 #include "chrome/browser/policy/message_util.h" 24 #include "chrome/browser/policy/message_util.h"
25 #include "chrome/browser/policy/policy_error_map.h" 25 #include "chrome/browser/policy/policy_error_map.h"
26 #include "chrome/browser/policy/policy_service.h" 26 #include "chrome/browser/policy/policy_map.h"
27 #include "chrome/browser/policy/policy_types.h"
27 #include "chrome/browser/policy/proto/device_management_backend.pb.h" 28 #include "chrome/browser/policy/proto/device_management_backend.pb.h"
28 #include "chrome/browser/prefs/pref_service.h"
29 #include "chrome/browser/profiles/profile.h" 29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/time_format.h" 30 #include "chrome/common/time_format.h"
32 #include "chrome/common/url_constants.h" 31 #include "chrome/common/url_constants.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/browser/web_ui.h" 32 #include "content/public/browser/web_ui.h"
35 #include "content/public/browser/web_ui_data_source.h" 33 #include "content/public/browser/web_ui_data_source.h"
34 #include "google_apis/gaia/gaia_auth_util.h"
36 #include "grit/browser_resources.h" 35 #include "grit/browser_resources.h"
37 #include "grit/generated_resources.h" 36 #include "grit/generated_resources.h"
37 #include "policy/policy_constants.h"
38 #include "ui/base/l10n/l10n_util.h" 38 #include "ui/base/l10n/l10n_util.h"
39 39
40 #if defined(OS_CHROMEOS) 40 #if defined(OS_CHROMEOS)
41 #include "chrome/browser/chromeos/login/user_manager.h" 41 #include "chrome/browser/chromeos/login/user_manager.h"
42 #include "chrome/browser/policy/device_cloud_policy_manager_chromeos.h" 42 #include "chrome/browser/policy/device_cloud_policy_manager_chromeos.h"
43 #include "chrome/browser/policy/device_local_account_policy_service.h" 43 #include "chrome/browser/policy/device_local_account_policy_service.h"
44 #include "chrome/browser/policy/user_cloud_policy_manager_chromeos.h" 44 #include "chrome/browser/policy/user_cloud_policy_manager_chromeos.h"
45 #else 45 #else
46 #include "chrome/browser/policy/user_cloud_policy_manager.h" 46 #include "chrome/browser/policy/user_cloud_policy_manager.h"
47 #include "chrome/browser/policy/user_cloud_policy_manager_factory.h" 47 #include "chrome/browser/policy/user_cloud_policy_manager_factory.h"
48 #endif 48 #endif
49 49
50 namespace em = enterprise_management; 50 namespace em = enterprise_management;
51 51
52 const char PolicyUIHandler::kLevel[] = "level";
53 const char PolicyUIHandler::kName[] = "name";
54 const char PolicyUIHandler::kScope[] = "scope";
55 const char PolicyUIHandler::kSet[] = "set";
56 const char PolicyUIHandler::kStatus[] = "status";
57 const char PolicyUIHandler::kValue[] = "value";
58
59 namespace { 52 namespace {
60 53
61 content::WebUIDataSource* CreatePolicyUIHTMLSource() { 54 content::WebUIDataSource* CreatePolicyUIHTMLSource() {
62 content::WebUIDataSource* source = 55 content::WebUIDataSource* source =
63 content::WebUIDataSource::Create(chrome::kChromeUIPolicyHost); 56 content::WebUIDataSource::Create(chrome::kChromeUIPolicyHost);
64 57
65 // Localized strings. 58 // Localized strings.
59 source->AddLocalizedString("title", IDS_POLICY_TITLE);
60 source->AddLocalizedString("filterPlaceholder",
61 IDS_POLICY_FILTER_PLACEHOLDER);
62 source->AddLocalizedString("reloadPolicies", IDS_POLICY_RELOAD_POLICIES);
63 source->AddLocalizedString("status", IDS_POLICY_STATUS);
64 source->AddLocalizedString("statusDevice", IDS_POLICY_STATUS_DEVICE);
65 source->AddLocalizedString("statusUser", IDS_POLICY_STATUS_USER);
66 source->AddLocalizedString("labelDomain", IDS_POLICY_LABEL_DOMAIN);
67 source->AddLocalizedString("labelUsername", IDS_POLICY_LABEL_USERNAME);
68 source->AddLocalizedString("labelClientId", IDS_POLICY_LABEL_CLIENT_ID);
69 source->AddLocalizedString("labelTimeSinceLastRefresh",
70 IDS_POLICY_LABEL_TIME_SINCE_LAST_REFRESH);
71 source->AddLocalizedString("labelRefreshInterval",
72 IDS_POLICY_LABEL_REFRESH_INTERVAL);
73 source->AddLocalizedString("labelStatus", IDS_POLICY_LABEL_STATUS);
74 source->AddLocalizedString("showUnset", IDS_POLICY_SHOW_UNSET);
75 source->AddLocalizedString("noPoliciesSet", IDS_POLICY_NO_POLICIES_SET);
76 source->AddLocalizedString("headerScope", IDS_POLICY_HEADER_SCOPE);
77 source->AddLocalizedString("headerLevel", IDS_POLICY_HEADER_LEVEL);
78 source->AddLocalizedString("headerName", IDS_POLICY_HEADER_NAME);
79 source->AddLocalizedString("headerValue", IDS_POLICY_HEADER_VALUE);
80 source->AddLocalizedString("headerStatus", IDS_POLICY_HEADER_STATUS);
81 source->AddLocalizedString("showExpandedValue",
82 IDS_POLICY_SHOW_EXPANDED_VALUE);
83 source->AddLocalizedString("hideExpandedValue",
84 IDS_POLICY_HIDE_EXPANDED_VALUE);
85 source->AddLocalizedString("scopeDevice", IDS_POLICY_SCOPE_DEVICE);
86 source->AddLocalizedString("scopeUser", IDS_POLICY_SCOPE_USER);
87 source->AddLocalizedString("levelRecommended", IDS_POLICY_LEVEL_RECOMMENDED);
88 source->AddLocalizedString("levelMandatory", IDS_POLICY_LEVEL_MANDATORY);
89 source->AddLocalizedString("ok", IDS_POLICY_OK);
90 source->AddLocalizedString("unset", IDS_POLICY_UNSET);
91 source->AddLocalizedString("unknown", IDS_POLICY_UNKNOWN);
92
66 source->SetUseJsonJSFormatV2(); 93 source->SetUseJsonJSFormatV2();
67 source->AddLocalizedString("policyTitle", IDS_POLICY_TITLE);
68 source->AddLocalizedString("statusPaneTitle", IDS_POLICY_STATUS_TITLE);
69 source->AddLocalizedString("fetchPoliciesText", IDS_POLICY_FETCH);
70 source->AddLocalizedString("devicePoliciesBoxTitle",
71 IDS_POLICY_DEVICE_POLICIES);
72 source->AddLocalizedString("userPoliciesBoxTitle", IDS_POLICY_USER_POLICIES);
73 source->AddLocalizedString("enrollmentDomainText",
74 IDS_POLICY_ENROLLMENT_DOMAIN);
75 source->AddLocalizedString("clientIdText", IDS_POLICY_CLIENT_ID);
76 source->AddLocalizedString("usernameText", IDS_POLICY_USERNAME);
77 source->AddLocalizedString("timeSinceLastFetchText", IDS_POLICY_LAST_FETCHED);
78 source->AddLocalizedString("fetchIntervalText", IDS_POLICY_FETCH_INTERVAL);
79 source->AddLocalizedString("serverStatusText", IDS_POLICY_SERVER_STATUS);
80 source->AddLocalizedString("showUnsentPoliciesText", IDS_POLICY_SHOW_UNSENT);
81 source->AddLocalizedString("filterPoliciesText", IDS_POLICY_FILTER);
82 source->AddLocalizedString("noPoliciesSet",IDS_POLICY_NO_POLICIES_SET);
83 source->AddLocalizedString("appliesToTableHeader", IDS_POLICY_APPLIES_TO);
84 source->AddLocalizedString("policyLevelTableHeader", IDS_POLICY_LEVEL);
85 source->AddLocalizedString("policyNameTableHeader", IDS_POLICY_ENTRY_NAME);
86 source->AddLocalizedString("policyValueTableHeader", IDS_POLICY_ENTRY_VALUE);
87 source->AddLocalizedString("policyStatusTableHeader",
88 IDS_POLICY_ENTRY_STATUS);
89 source->AddLocalizedString("showMoreText", IDS_POLICY_SHOW_MORE);
90 source->AddLocalizedString("hideText", IDS_POLICY_HIDE);
91 source->SetJsonPath("strings.js"); 94 source->SetJsonPath("strings.js");
92 95
93 // Add required resources. 96 // Add required resources.
94 source->AddResourcePath("policy.css", IDR_POLICY_CSS); 97 source->AddResourcePath("policy.css", IDR_POLICY_CSS);
95 source->AddResourcePath("policy.js", IDR_POLICY_JS); 98 source->AddResourcePath("policy.js", IDR_POLICY_JS);
99 source->AddResourcePath("uber_utils.js", IDR_UBER_UTILS_JS);
96 source->SetDefaultResource(IDR_POLICY_HTML); 100 source->SetDefaultResource(IDR_POLICY_HTML);
97 101
98 return source; 102 return source;
99 } 103 }
100 104
101 string16 GetPolicyScopeString(policy::PolicyScope scope) { 105 void GetStatusFromCore(const policy::CloudPolicyCore* core,
102 switch (scope) { 106 base::DictionaryValue* dict) {
103 case policy::POLICY_SCOPE_USER: 107 const policy::CloudPolicyStore* store = core->store();
104 return l10n_util::GetStringUTF16(IDS_POLICY_SCOPE_USER); 108 const policy::CloudPolicyClient* client = core->client();
105 case policy::POLICY_SCOPE_MACHINE: 109 const policy::CloudPolicyRefreshScheduler* refresh_scheduler =
106 return l10n_util::GetStringUTF16(IDS_POLICY_SCOPE_MACHINE); 110 core->refresh_scheduler();
107 } 111
108 NOTREACHED(); 112 bool no_error = store->status() == policy::CloudPolicyStore::STATUS_OK &&
109 return string16(); 113 client && client->status() == policy::DM_STATUS_SUCCESS;
114 string16 status = store->status() == policy::CloudPolicyStore::STATUS_OK &&
115 client && client->status() != policy::DM_STATUS_SUCCESS ?
116 policy::FormatDeviceManagementStatus(client->status()) :
117 policy::FormatStoreStatus(store->status(),
118 store->validation_status());
119 const em::PolicyData* policy = store->policy();
120 std::string client_id = policy ? policy->device_id() : std::string();
121 std::string username = policy ? policy->username() : std::string();
122 base::TimeDelta refresh_interval =
123 base::TimeDelta::FromMilliseconds(refresh_scheduler ?
124 refresh_scheduler->refresh_delay() :
125 policy::CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs);
126 base::Time last_refresh_time = refresh_scheduler ?
127 refresh_scheduler->last_refresh() : base::Time();
128
129 dict->SetBoolean("error", !no_error);
130 dict->SetString("status", status);
131 dict->SetString("clientId", client_id);
132 dict->SetString("username", username);
133 dict->SetString("refreshInterval",
134 TimeFormat::TimeRemainingShort(refresh_interval));
135 dict->SetString("timeSinceLastRefresh", last_refresh_time.is_null() ?
136 l10n_util::GetStringUTF16(IDS_POLICY_NEVER_FETCHED) :
137 TimeFormat::TimeElapsed(base::Time::NowFromSystemTime() -
138 last_refresh_time));
110 } 139 }
111 140
112 string16 GetPolicyLevelString(policy::PolicyLevel level) { 141 void ExtractDomainFromUsername(base::DictionaryValue* dict) {
113 switch (level) { 142 std::string username;
114 case policy::POLICY_LEVEL_RECOMMENDED: 143 dict->GetString("username", &username);
115 return l10n_util::GetStringUTF16(IDS_POLICY_LEVEL_RECOMMENDED); 144 if (!username.empty())
116 case policy::POLICY_LEVEL_MANDATORY: 145 dict->SetString("domain", gaia::ExtractDomainName(username));
117 return l10n_util::GetStringUTF16(IDS_POLICY_LEVEL_MANDATORY);
118 }
119 NOTREACHED();
120 return string16();
121 }
122
123 base::DictionaryValue* GetPolicyDetails(
124 const policy::PolicyDefinitionList::Entry* policy_definition,
125 const policy::PolicyMap::Entry* policy_value,
126 const string16& error_message) {
127 base::DictionaryValue* details = new base::DictionaryValue();
128 details->SetString(PolicyUIHandler::kName,
129 ASCIIToUTF16(policy_definition->name));
130 details->SetBoolean(PolicyUIHandler::kSet, true);
131 details->SetString(PolicyUIHandler::kLevel,
132 GetPolicyLevelString(policy_value->level));
133 details->SetString(PolicyUIHandler::kScope,
134 GetPolicyScopeString(policy_value->scope));
135 details->Set(PolicyUIHandler::kValue, policy_value->value->DeepCopy());
136 if (error_message.empty()) {
137 details->SetString(PolicyUIHandler::kStatus,
138 l10n_util::GetStringUTF16(IDS_OK));
139 } else {
140 details->SetString(PolicyUIHandler::kStatus, error_message);
141 }
142 return details;
143 }
144
145 base::DictionaryValue* GetPolicyErrorDetails(const std::string& policy_name,
146 bool is_set) {
147 base::DictionaryValue* details = new base::DictionaryValue();
148 details->SetString(PolicyUIHandler::kName, ASCIIToUTF16(policy_name));
149 details->SetBoolean(PolicyUIHandler::kSet, is_set);
150 details->SetString(PolicyUIHandler::kLevel, "");
151 details->SetString(PolicyUIHandler::kScope, "");
152 details->SetString(PolicyUIHandler::kValue, "");
153 if (is_set)
154 details->SetString(PolicyUIHandler::kStatus,
155 l10n_util::GetStringUTF16(IDS_POLICY_UNKNOWN));
156 else
157 details->SetString(PolicyUIHandler::kStatus,
158 l10n_util::GetStringUTF16(IDS_POLICY_NOT_SET));
159 return details;
160 } 146 }
161 147
162 } // namespace 148 } // namespace
163 149
164 // An interface for querying status data. 150 // An interface for querying the status of cloud policy.
165 class CloudPolicyStatusProvider { 151 class CloudPolicyStatusProvider {
166 public: 152 public:
167 CloudPolicyStatusProvider() {} 153 CloudPolicyStatusProvider();
168 virtual ~CloudPolicyStatusProvider() {} 154 virtual ~CloudPolicyStatusProvider();
169 155
170 // Sets a callback to invoke upon status changes. 156 // Sets a callback to invoke upon status changes.
171 void SetStatusChangeCallback(const base::Closure& callback) { 157 void SetStatusChangeCallback(const base::Closure& callback);
172 callback_ = callback;
173 }
174 158
175 // Fills |status_dict| and returns true if the status is relevant. 159 virtual void GetStatus(base::DictionaryValue* dict);
176 virtual bool GetStatus(base::DictionaryValue* status_dict) {
177 return false;
178 }
179 160
180 protected: 161 protected:
181 void NotifyStatusChange() { 162 void NotifyStatusChange();
182 if (!callback_.is_null())
183 callback_.Run();
184 }
185 163
186 private: 164 private:
187 base::Closure callback_; 165 base::Closure callback_;
188 166
189 DISALLOW_COPY_AND_ASSIGN(CloudPolicyStatusProvider); 167 DISALLOW_COPY_AND_ASSIGN(CloudPolicyStatusProvider);
190 }; 168 };
191 169
192 // Status provider implementation that pulls cloud policy status from a 170 // Status provider implementation that pulls cloud policy status from a
193 // CloudPolicyCore instance provided at construction time. Also listens for 171 // CloudPolicyCore instance provided at construction time. Also listens for
194 // changes on that CloudPolicyCore and reports them through the status change 172 // changes on that CloudPolicyCore and reports them through the status change
195 // callback. 173 // callback.
196 class CloudPolicyCoreStatusProvider 174 class CloudPolicyCoreStatusProvider
197 : public CloudPolicyStatusProvider, 175 : public CloudPolicyStatusProvider,
198 public policy::CloudPolicyStore::Observer { 176 public policy::CloudPolicyStore::Observer {
199 public: 177 public:
200 explicit CloudPolicyCoreStatusProvider(policy::CloudPolicyCore* core) 178 explicit CloudPolicyCoreStatusProvider(policy::CloudPolicyCore* core);
201 : core_(core) { 179 virtual ~CloudPolicyCoreStatusProvider();
202 core_->store()->AddObserver(this);
203 }
204 virtual ~CloudPolicyCoreStatusProvider() {
205 core_->store()->RemoveObserver(this);
206 }
207
208 // CloudPolicyStatusProvider:
209 virtual bool GetStatus(base::DictionaryValue* status_dict) OVERRIDE {
210 return GetStatusFromCore(core_, status_dict);
211 }
212 180
213 // policy::CloudPolicyStore::Observer: 181 // policy::CloudPolicyStore::Observer:
214 virtual void OnStoreLoaded(policy::CloudPolicyStore* store) OVERRIDE { 182 virtual void OnStoreLoaded(policy::CloudPolicyStore* store) OVERRIDE;
215 NotifyStatusChange(); 183 virtual void OnStoreError(policy::CloudPolicyStore* store) OVERRIDE;
216 }
217 virtual void OnStoreError(policy::CloudPolicyStore* store) OVERRIDE {
218 NotifyStatusChange();
219 }
220 184
221 // Extracts status information from |core| and writes it to |status_dict|. 185 protected:
222 static bool GetStatusFromCore(policy::CloudPolicyCore* core,
223 base::DictionaryValue* status_dict);
224
225 private:
226 // Policy status is read from the CloudPolicyClient, CloudPolicyStore and 186 // Policy status is read from the CloudPolicyClient, CloudPolicyStore and
227 // CloudPolicyRefreshScheduler hosted by this |core_|. 187 // CloudPolicyRefreshScheduler hosted by this |core_|.
228 policy::CloudPolicyCore* core_; 188 policy::CloudPolicyCore* core_;
229 189
230 DISALLOW_COPY_AND_ASSIGN(CloudPolicyCoreStatusProvider); 190 DISALLOW_COPY_AND_ASSIGN(CloudPolicyCoreStatusProvider);
231 }; 191 };
232 192
233 // static 193 // A cloud policy status provider for user policy.
234 bool CloudPolicyCoreStatusProvider::GetStatusFromCore( 194 class UserPolicyStatusProvider : public CloudPolicyCoreStatusProvider {
235 policy::CloudPolicyCore* core, 195 public:
236 base::DictionaryValue* status_dict) { 196 explicit UserPolicyStatusProvider(policy::CloudPolicyCore* core);
237 policy::CloudPolicyStore* store = core->store(); 197 virtual ~UserPolicyStatusProvider();
238 policy::CloudPolicyClient* client = core->client();
239 policy::CloudPolicyRefreshScheduler* refresh_scheduler =
240 core->refresh_scheduler();
241 198
242 string16 status; 199 // CloudPolicyCoreStatusProvider:
243 if (store->status() == policy::CloudPolicyStore::STATUS_OK && 200 virtual void GetStatus(base::DictionaryValue* dict) OVERRIDE;
244 client && client->status() != policy::DM_STATUS_SUCCESS) {
245 status = policy::FormatDeviceManagementStatus(client->status());
246 } else {
247 status = policy::FormatStoreStatus(store->status(),
248 store->validation_status());
249 }
250 status_dict->SetString("statusMessage", status);
251 201
252 base::Time last_refresh_time; 202 private:
253 if (refresh_scheduler) 203 DISALLOW_COPY_AND_ASSIGN(UserPolicyStatusProvider);
254 last_refresh_time = refresh_scheduler->last_refresh(); 204 };
255 status_dict->SetString(
256 "timeSinceLastFetch",
257 last_refresh_time.is_null() ?
258 l10n_util::GetStringUTF16(IDS_POLICY_NEVER_FETCHED) :
259 TimeFormat::TimeElapsed(base::Time::NowFromSystemTime() -
260 last_refresh_time));
261
262 const em::PolicyData* policy = store->policy();
263 status_dict->SetString(
264 "clientId", policy ? ASCIIToUTF16(policy->device_id()) : string16());
265 status_dict->SetString(
266 "user", policy ? UTF8ToUTF16(policy->username()) : string16());
267
268 int64 interval = policy::CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs;
269 if (refresh_scheduler)
270 interval = refresh_scheduler->refresh_delay();
271 status_dict->SetString(
272 "fetchInterval",
273 TimeFormat::TimeRemainingShort(
274 base::TimeDelta::FromMilliseconds(interval)));
275
276 return store->is_managed();
277 }
278 205
279 #if defined(OS_CHROMEOS) 206 #if defined(OS_CHROMEOS)
207 // A cloud policy status provider for device policy.
208 class DevicePolicyStatusProvider : public CloudPolicyCoreStatusProvider {
209 public:
210 explicit DevicePolicyStatusProvider(
211 policy::BrowserPolicyConnector* connector);
212 virtual ~DevicePolicyStatusProvider();
213
214 // CloudPolicyCoreStatusProvider:
215 virtual void GetStatus(base::DictionaryValue* dict) OVERRIDE;
216
217 private:
218 std::string domain_;
219
220 DISALLOW_COPY_AND_ASSIGN(DevicePolicyStatusProvider);
221 };
222
280 // A cloud policy status provider that reads policy status from the policy core 223 // A cloud policy status provider that reads policy status from the policy core
281 // associated with the device-local account specified by |account_id| at 224 // associated with the device-local account specified by |account_id| at
282 // construction time. The indirection via account ID and 225 // construction time. The indirection via account ID and
283 // DeviceLocalAccountPolicyService is necessary because the device-local account 226 // DeviceLocalAccountPolicyService is necessary because the device-local account
284 // may go away any time behind the scenes, at which point the status message 227 // may go away any time behind the scenes, at which point the status message
285 // text will indicate CloudPolicyStore::STATUS_BAD_STATE. 228 // text will indicate CloudPolicyStore::STATUS_BAD_STATE.
286 class DeviceLocalAccountPolicyStatusProvider 229 class DeviceLocalAccountPolicyStatusProvider
287 : public CloudPolicyStatusProvider, 230 : public CloudPolicyStatusProvider,
288 public policy::DeviceLocalAccountPolicyService::Observer { 231 public policy::DeviceLocalAccountPolicyService::Observer {
289 public: 232 public:
290 DeviceLocalAccountPolicyStatusProvider( 233 DeviceLocalAccountPolicyStatusProvider(
291 const std::string& account_id, 234 const std::string& account_id,
292 policy::DeviceLocalAccountPolicyService* service) 235 policy::DeviceLocalAccountPolicyService* service);
293 : account_id_(account_id), 236 virtual ~DeviceLocalAccountPolicyStatusProvider();
294 service_(service) {
295 service_->AddObserver(this);
296 }
297 virtual ~DeviceLocalAccountPolicyStatusProvider() {
298 service_->RemoveObserver(this);
299 }
300 237
301 // CloudPolicyStatusProvider: 238 // CloudPolicyStatusProvider:
302 virtual bool GetStatus(base::DictionaryValue* status_dict) OVERRIDE { 239 virtual void GetStatus(base::DictionaryValue* dict) OVERRIDE;
303 policy::DeviceLocalAccountPolicyBroker* broker =
304 service_->GetBrokerForAccount(account_id_);
305 if (broker) {
306 return CloudPolicyCoreStatusProvider::GetStatusFromCore(broker->core(),
307 status_dict);
308 }
309
310 status_dict->SetString("user", account_id_);
311 status_dict->SetString(
312 "statusMessage",
313 policy::FormatStoreStatus(
314 policy::CloudPolicyStore::STATUS_BAD_STATE,
315 policy::CloudPolicyValidatorBase::VALIDATION_OK));
316 return true;
317 }
318 240
319 // policy::DeviceLocalAccountPolicyService::Observer: 241 // policy::DeviceLocalAccountPolicyService::Observer:
320 virtual void OnPolicyUpdated(const std::string& account_id) OVERRIDE { 242 virtual void OnPolicyUpdated(const std::string& account_id) OVERRIDE;
321 if (account_id == account_id_) 243 virtual void OnDeviceLocalAccountsChanged() OVERRIDE;
322 NotifyStatusChange();
323 }
324 virtual void OnDeviceLocalAccountsChanged() OVERRIDE {
325 NotifyStatusChange();
326 }
327 244
328 private: 245 private:
329 const std::string account_id_; 246 const std::string account_id_;
330 policy::DeviceLocalAccountPolicyService* service_; 247 policy::DeviceLocalAccountPolicyService* service_;
331 248
332 DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyStatusProvider); 249 DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyStatusProvider);
333 }; 250 };
334 #endif 251 #endif
335 252
336 //////////////////////////////////////////////////////////////////////////////// 253 CloudPolicyStatusProvider::CloudPolicyStatusProvider() {
337 // 254 }
338 // PolicyUIHandler
339 //
340 ////////////////////////////////////////////////////////////////////////////////
341 255
342 PolicyUIHandler::PolicyUIHandler() 256 CloudPolicyStatusProvider::~CloudPolicyStatusProvider() {
343 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {} 257 }
344 258
345 PolicyUIHandler::~PolicyUIHandler() { 259 void CloudPolicyStatusProvider::SetStatusChangeCallback(
260 const base::Closure& callback) {
261 callback_ = callback;
262 }
263
264 void CloudPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
265 }
266
267 void CloudPolicyStatusProvider::NotifyStatusChange() {
268 if (!callback_.is_null())
269 callback_.Run();
270 }
271
272 CloudPolicyCoreStatusProvider::CloudPolicyCoreStatusProvider(
273 policy::CloudPolicyCore* core) : core_(core) {
274 core_->store()->AddObserver(this);
275 // TODO(bartfab): Add an observer that watches for client errors. Observing
276 // core_->client() directly is not safe as the client may be destroyed and
277 // (re-)created anytime if the user signs in or out on desktop platforms.
278 }
279
280 CloudPolicyCoreStatusProvider::~CloudPolicyCoreStatusProvider() {
281 core_->store()->RemoveObserver(this);
282 }
283
284 void CloudPolicyCoreStatusProvider::OnStoreLoaded(
285 policy::CloudPolicyStore* store) {
286 NotifyStatusChange();
287 }
288
289 void CloudPolicyCoreStatusProvider::OnStoreError(
290 policy::CloudPolicyStore* store) {
291 NotifyStatusChange();
292 }
293
294 UserPolicyStatusProvider::UserPolicyStatusProvider(
295 policy::CloudPolicyCore* core) : CloudPolicyCoreStatusProvider(core) {
296 }
297
298 UserPolicyStatusProvider::~UserPolicyStatusProvider() {
299 }
300
301 void UserPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
302 if (!core_->store()->is_managed())
303 return;
304 GetStatusFromCore(core_, dict);
305 ExtractDomainFromUsername(dict);
306 }
307
308 #if defined(OS_CHROMEOS)
309 DevicePolicyStatusProvider::DevicePolicyStatusProvider(
310 policy::BrowserPolicyConnector* connector)
311 : CloudPolicyCoreStatusProvider(
312 connector->GetDeviceCloudPolicyManager()->core()) {
313 domain_ = connector->GetEnterpriseDomain();
314 }
315
316 DevicePolicyStatusProvider::~DevicePolicyStatusProvider() {
317 }
318
319 void DevicePolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
320 dict->SetString("domain", domain_);
Mattias Nissler (ping if slow) 2013/01/30 16:34:59 Shouldn't this call GetStatusFromCore(core_) as we
bartfab (slow) 2013/01/31 14:24:17 Yep. I discovered this as well when I visited chro
321 }
322
323 DeviceLocalAccountPolicyStatusProvider::DeviceLocalAccountPolicyStatusProvider(
324 const std::string& account_id,
325 policy::DeviceLocalAccountPolicyService* service)
326 : account_id_(account_id),
327 service_(service) {
328 service_->AddObserver(this);
329 }
330
331 DeviceLocalAccountPolicyStatusProvider::
332 ~DeviceLocalAccountPolicyStatusProvider() {
333 service_->RemoveObserver(this);
334 }
335
336 void DeviceLocalAccountPolicyStatusProvider::GetStatus(
337 base::DictionaryValue* dict) {
338 const policy::DeviceLocalAccountPolicyBroker* broker =
339 service_->GetBrokerForAccount(account_id_);
340 if (broker) {
341 GetStatusFromCore(broker->core(), dict);
342 } else {
343 dict->SetBoolean("error", true);
344 dict->SetString("status",
345 policy::FormatStoreStatus(
346 policy::CloudPolicyStore::STATUS_BAD_STATE,
347 policy::CloudPolicyValidatorBase::VALIDATION_OK));
348 dict->SetString("username", account_id_);
349 }
350 ExtractDomainFromUsername(dict);
351 dict->SetBoolean("publicAccount", true);
352 }
353
354 void DeviceLocalAccountPolicyStatusProvider::OnPolicyUpdated(
355 const std::string& account_id) {
356 if (account_id == account_id_)
357 NotifyStatusChange();
358 }
359
360 void DeviceLocalAccountPolicyStatusProvider::OnDeviceLocalAccountsChanged() {
361 NotifyStatusChange();
362 }
363 #endif
364
365 PolicyUIHandlerBase::PolicyUIHandlerBase()
366 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
367 }
368
369 PolicyUIHandlerBase::~PolicyUIHandlerBase() {
346 GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this); 370 GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
347 } 371 }
348 372
349 void PolicyUIHandler::RegisterMessages() { 373 void PolicyUIHandlerBase::RegisterMessages() {
350 #if defined(OS_CHROMEOS) 374 #if defined(OS_CHROMEOS)
351 policy::BrowserPolicyConnector* connector = 375 policy::BrowserPolicyConnector* connector =
352 g_browser_process->browser_policy_connector(); 376 g_browser_process->browser_policy_connector();
353 if (connector->IsEnterpriseManaged()) { 377 if (connector->IsEnterpriseManaged())
354 enterprise_domain_ = UTF8ToUTF16(connector->GetEnterpriseDomain()); 378 device_status_provider_.reset(new DevicePolicyStatusProvider(connector));
355 device_status_provider_.reset(
356 new CloudPolicyCoreStatusProvider(
357 connector->GetDeviceCloudPolicyManager()->core()));
358 }
359 379
360 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); 380 const chromeos::UserManager* user_manager = chromeos::UserManager::Get();
361 if (user_manager->IsLoggedInAsPublicAccount()) { 381 if (user_manager->IsLoggedInAsPublicAccount()) {
362 policy::DeviceLocalAccountPolicyService* local_account_service = 382 policy::DeviceLocalAccountPolicyService* local_account_service =
363 connector->GetDeviceLocalAccountPolicyService(); 383 connector->GetDeviceLocalAccountPolicyService();
364 if (local_account_service) { 384 if (local_account_service) {
365 user_status_provider_.reset( 385 user_status_provider_.reset(
366 new DeviceLocalAccountPolicyStatusProvider( 386 new DeviceLocalAccountPolicyStatusProvider(
367 user_manager->GetLoggedInUser()->email(), local_account_service)); 387 user_manager->GetLoggedInUser()->email(), local_account_service));
368 } 388 }
369 } else { 389 } else {
Mattias Nissler (ping if slow) 2013/01/30 16:34:59 remove stray space
bartfab (slow) 2013/01/31 14:24:17 Done.
370 policy::CloudPolicyManager* user_cloud_policy_manager = 390 policy::CloudPolicyManager* user_cloud_policy_manager =
371 connector->GetUserCloudPolicyManager(); 391 connector->GetUserCloudPolicyManager();
372 if (user_cloud_policy_manager) { 392 if (user_cloud_policy_manager) {
373 user_status_provider_.reset( 393 user_status_provider_.reset(
374 new CloudPolicyCoreStatusProvider(user_cloud_policy_manager->core())); 394 new CloudPolicyCoreStatusProvider(user_cloud_policy_manager->core()));
375 } 395 }
376 } 396 }
377 #else 397 #else
378 policy::CloudPolicyManager* user_cloud_policy_manager = 398 policy::CloudPolicyManager* user_cloud_policy_manager =
379 policy::UserCloudPolicyManagerFactory::GetForProfile( 399 policy::UserCloudPolicyManagerFactory::GetForProfile(
380 Profile::FromWebUI(web_ui())); 400 Profile::FromWebUI(web_ui()));
381 if (user_cloud_policy_manager) { 401 if (user_cloud_policy_manager) {
382 user_status_provider_.reset( 402 user_status_provider_.reset(
383 new CloudPolicyCoreStatusProvider(user_cloud_policy_manager->core())); 403 new UserPolicyStatusProvider(user_cloud_policy_manager->core()));
384 } 404 }
385 #endif 405 #endif
386 406
387 if (!user_status_provider_.get()) 407 if (!user_status_provider_.get())
388 user_status_provider_.reset(new CloudPolicyStatusProvider()); 408 user_status_provider_.reset(new CloudPolicyStatusProvider());
389 if (!device_status_provider_.get()) 409 if (!device_status_provider_.get())
390 device_status_provider_.reset(new CloudPolicyStatusProvider()); 410 device_status_provider_.reset(new CloudPolicyStatusProvider());
391 411
392 base::Closure update_callback(base::Bind(&PolicyUIHandler::SendDataToUI, 412 base::Closure update_callback(base::Bind(&PolicyUIHandlerBase::SendStatus,
393 base::Unretained(this))); 413 base::Unretained(this)));
394 user_status_provider_->SetStatusChangeCallback(update_callback); 414 user_status_provider_->SetStatusChangeCallback(update_callback);
395 device_status_provider_->SetStatusChangeCallback(update_callback); 415 device_status_provider_->SetStatusChangeCallback(update_callback);
396 GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_CHROME, this); 416 GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
397 417
398 web_ui()->RegisterMessageCallback( 418 web_ui()->RegisterMessageCallback(
399 "requestData", 419 "initialized",
400 base::Bind(&PolicyUIHandler::HandleRequestData, 420 base::Bind(&PolicyUIHandlerBase::HandleInitialized,
401 base::Unretained(this))); 421 base::Unretained(this)));
402 web_ui()->RegisterMessageCallback( 422 web_ui()->RegisterMessageCallback(
403 "fetchPolicy", 423 "reloadPolicies",
404 base::Bind(&PolicyUIHandler::HandleFetchPolicy, 424 base::Bind(&PolicyUIHandlerBase::HandleReloadPolicies,
405 base::Unretained(this))); 425 base::Unretained(this)));
406 } 426 }
407 427
408 void PolicyUIHandler::OnPolicyUpdated(policy::PolicyDomain domain, 428 void PolicyUIHandlerBase::OnPolicyUpdated(policy::PolicyDomain domain,
409 const std::string& component_id, 429 const std::string& component_id,
410 const policy::PolicyMap& previous, 430 const policy::PolicyMap& previous,
411 const policy::PolicyMap& current) { 431 const policy::PolicyMap& current) {
412 DCHECK(domain == policy::POLICY_DOMAIN_CHROME); 432 DCHECK(domain == policy::POLICY_DOMAIN_CHROME);
413 DCHECK(component_id.empty()); 433 DCHECK(component_id.empty());
414 SendDataToUI(); 434 SendPolicyValues();
415 } 435 }
416 436
417 // static 437 void PolicyUIHandlerBase::SendPolicyValues() const {
418 scoped_ptr<base::ListValue> PolicyUIHandler::GetPolicyStatusList(
419 const policy::PolicyMap& policies,
420 bool* any_policies_set) {
421 scoped_ptr<base::ListValue> result(new base::ListValue());
422 // Make a copy that can be modified, since some policy values are modified 438 // Make a copy that can be modified, since some policy values are modified
423 // before being displayed. 439 // before being displayed.
424 policy::PolicyMap policy_map; 440 policy::PolicyMap map;
425 policy_map.CopyFrom(policies); 441 map.CopyFrom(GetPolicyService()->GetPolicies(policy::POLICY_DOMAIN_CHROME,
442 std::string()));
426 443
427 // Get a list of all the errors in the policy values. 444 // Get a list of all the errors in the policy values.
428 const policy::ConfigurationPolicyHandlerList* handler_list = 445 const policy::ConfigurationPolicyHandlerList* handler_list =
429 g_browser_process->browser_policy_connector()->GetHandlerList(); 446 g_browser_process->browser_policy_connector()->GetHandlerList();
430 policy::PolicyErrorMap errors; 447 policy::PolicyErrorMap errors;
431 handler_list->ApplyPolicySettings(policy_map, NULL, &errors); 448 handler_list->ApplyPolicySettings(map, NULL, &errors);
432 handler_list->PrepareForDisplaying(&policy_map);
433 449
434 // Append each known policy and its status. 450 // Convert dictionary values to strings for display.
435 *any_policies_set = false; 451 handler_list->PrepareForDisplaying(&map);
436 std::vector<base::DictionaryValue*> unset_policies; 452
437 base::hash_set<std::string> known_names; 453 base::DictionaryValue values;
438 const policy::PolicyDefinitionList* policy_list = 454 for (policy::PolicyMap::const_iterator entry = map.begin();
439 policy::GetChromePolicyDefinitionList(); 455 entry != map.end(); ++entry) {
440 for (const policy::PolicyDefinitionList::Entry* policy = policy_list->begin; 456 base::DictionaryValue* value = new base::DictionaryValue;
441 policy != policy_list->end; ++policy) { 457 value->Set("value", entry->second.value->DeepCopy());
442 known_names.insert(policy->name); 458 if (entry->second.scope == policy::POLICY_SCOPE_MACHINE)
443 const policy::PolicyMap::Entry* entry = policy_map.Get(policy->name); 459 value->SetBoolean("device", true);
Mattias Nissler (ping if slow) 2013/01/30 16:34:59 suggestion: For clarity, you could set a string pr
bartfab (slow) 2013/01/31 14:24:17 Done.
444 string16 error_message = errors.GetErrors(policy->name); 460 if (entry->second.level == policy::POLICY_LEVEL_RECOMMENDED)
445 if (entry) { 461 value->SetBoolean("recommended", true);
Mattias Nissler (ping if slow) 2013/01/30 16:34:59 same here: "level" -> "recommended" or "mandatory"
bartfab (slow) 2013/01/31 14:24:17 Done.
446 *any_policies_set = true; 462 string16 error = errors.GetErrors(entry->first);
447 result->Append(GetPolicyDetails(policy, entry, error_message)); 463 if (!error.empty())
448 } else { 464 value->SetString("error", error);
449 unset_policies.push_back(GetPolicyErrorDetails(policy->name, false)); 465 values.Set(entry->first, value);
450 }
451 } 466 }
452 467
453 // Append unrecognized policy names. 468 web_ui()->CallJavascriptFunction("policy.Page.setPolicyValues", values);
454 for (policy::PolicyMap::const_iterator it = policy_map.begin();
455 it != policy_map.end(); ++it) {
456 if (!ContainsKey(known_names, it->first))
457 result->Append(GetPolicyErrorDetails(it->first, true));
458 }
459
460 // Append policies that aren't currently configured to the end.
461 for (std::vector<base::DictionaryValue*>::const_iterator it =
462 unset_policies.begin();
463 it != unset_policies.end(); ++it) {
464 result->Append(*it);
465 }
466
467 return result.Pass();
468 } 469 }
469 470
470 void PolicyUIHandler::HandleRequestData(const base::ListValue* args) { 471 void PolicyUIHandlerBase::SendStatus() const {
471 SendDataToUI(); 472 scoped_ptr<base::DictionaryValue> device_status(new base::DictionaryValue);
473 device_status_provider_->GetStatus(device_status.get());
474 if (!device_domain_.empty())
475 device_status->SetString("domain", device_domain_);
Mattias Nissler (ping if slow) 2013/01/30 16:34:59 unused?
bartfab (slow) 2013/01/31 14:24:17 Used for device policy. For user policy, the usern
476 scoped_ptr<base::DictionaryValue> user_status(new base::DictionaryValue);
477 user_status_provider_->GetStatus(user_status.get());
478 std::string username;
479 user_status->GetString("username", &username);
480 if (!username.empty())
481 user_status->SetString("domain", gaia::ExtractDomainName(username));
482
483 base::DictionaryValue status;
484 if (!device_status->empty())
485 status.Set("device", device_status.release());
486 if (!user_status->empty())
487 status.Set("user", user_status.release());
488
489 web_ui()->CallJavascriptFunction("policy.Page.setStatus", status);
472 } 490 }
473 491
474 void PolicyUIHandler::HandleFetchPolicy(const base::ListValue* args) { 492 void PolicyUIHandlerBase::HandleInitialized(const base::ListValue* args) {
475 // Fetching policy can potentially take a while due to cloud policy fetches. 493 SendDefinitions();
476 // Use a WeakPtr to make sure the callback is invalidated if the tab is closed 494 SendPolicyValues();
477 // before the fetching completes. 495 SendStatus();
496 }
497
498 void PolicyUIHandlerBase::HandleReloadPolicies(const base::ListValue* args) {
478 GetPolicyService()->RefreshPolicies( 499 GetPolicyService()->RefreshPolicies(
479 base::Bind(&PolicyUIHandler::OnRefreshDone, 500 base::Bind(&PolicyUIHandlerBase::OnRefreshPoliciesDone,
480 weak_factory_.GetWeakPtr())); 501 weak_factory_.GetWeakPtr()));
481 } 502 }
482 503
483 void PolicyUIHandler::OnRefreshDone() { 504 void PolicyUIHandlerBase::OnRefreshPoliciesDone() const {
484 web_ui()->CallJavascriptFunction("Policy.refreshDone"); 505 web_ui()->CallJavascriptFunction("policy.Page.reloadPoliciesDone");
485 } 506 }
486 507
487 void PolicyUIHandler::SendDataToUI() { 508 policy::PolicyService* PolicyUIHandlerBase::GetPolicyService() const {
488 policy::PolicyService* service = GetPolicyService(); 509 return Profile::FromWebUI(web_ui())->GetPolicyService();
489 bool any_policies_set = false;
490 base::ListValue* list =
491 GetPolicyStatusList(
492 service->GetPolicies(policy::POLICY_DOMAIN_CHROME, std::string()),
493 &any_policies_set).release();
494 base::DictionaryValue results;
495 results.Set("policies", list);
496 results.SetBoolean("anyPoliciesSet", any_policies_set);
497 base::DictionaryValue* dict = GetStatusData();
498 results.Set("status", dict);
499 web_ui()->CallJavascriptFunction("Policy.returnData", results);
500 } 510 }
501 511
502 policy::PolicyService* PolicyUIHandler::GetPolicyService() { 512 PolicyUIHandler::PolicyUIHandler() {
503 Profile* profile = Profile::FromWebUI(web_ui());
504 return profile->GetPolicyService();
505 } 513 }
506 514
507 base::DictionaryValue* PolicyUIHandler::GetStatusData() { 515 PolicyUIHandler::~PolicyUIHandler() {
508 base::DictionaryValue* results = new base::DictionaryValue();
509
510 scoped_ptr<base::DictionaryValue> user_status_dict(
511 new base::DictionaryValue());
512 bool user_status_available =
513 user_status_provider_->GetStatus(user_status_dict.get());
514 user_status_dict->SetBoolean("display", user_status_available);
515 results->Set("userStatus", user_status_dict.release());
516
517 scoped_ptr<base::DictionaryValue> device_status_dict(
518 new base::DictionaryValue());
519 bool device_status_available =
520 device_status_provider_->GetStatus(device_status_dict.get());
521 device_status_dict->SetString("domain", enterprise_domain_);
522 device_status_dict->SetBoolean("display", device_status_available);
523 results->Set("deviceStatus", device_status_dict.release());
524
525 results->SetBoolean("displayStatusSection",
526 user_status_available || device_status_available);
527 return results;
528 } 516 }
529 517
530 //////////////////////////////////////////////////////////////////////////////// 518 void PolicyUIHandler::SendDefinitions() const {
531 // 519 base::DictionaryValue definitions;
532 // PolicyUI 520 const policy::PolicyDefinitionList* list =
533 // 521 policy::GetChromePolicyDefinitionList();
534 //////////////////////////////////////////////////////////////////////////////// 522 for (const policy::PolicyDefinitionList::Entry* entry = list->begin;
523 entry != list->end; ++entry) {
524 definitions.SetBoolean(entry->name, true);
Mattias Nissler (ping if slow) 2013/01/30 16:34:59 Why not send a list of names?
bartfab (slow) 2013/01/31 14:24:17 It similifies the JS code. A dict allows looping (
525 }
526 web_ui()->CallJavascriptFunction("policy.Page.setDefinitions", definitions);
527 }
535 528
536 PolicyUI::PolicyUI(content::WebUI* web_ui) : WebUIController(web_ui) { 529 PolicyUI::PolicyUI(content::WebUI* web_ui) : WebUIController(web_ui) {
537 web_ui->AddMessageHandler(new PolicyUIHandler); 530 web_ui->AddMessageHandler(new PolicyUIHandler);
538 531 content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
539 // Set up the chrome://policy/ source. 532 CreatePolicyUIHTMLSource());
540 Profile* profile = Profile::FromWebUI(web_ui);
541 content::WebUIDataSource::Add(profile, CreatePolicyUIHTMLSource());
542 } 533 }
543 534
544 PolicyUI::~PolicyUI() { 535 PolicyUI::~PolicyUI() {
545 } 536 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698