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

Side by Side Diff: chrome/browser/resources/md_user_manager/create_profile.js

Issue 2939273002: DO NOT SUBMIT: what chrome/browser/resources/ could eventually look like with clang-format (Closed)
Patch Set: Created 3 years, 6 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 /**
6 * @fileoverview 'create-profile' is a page that contains controls for creating 6 * @fileoverview 'create-profile' is a page that contains controls for creating
7 * a (optionally supervised) profile, including choosing a name, and an avatar. 7 * a (optionally supervised) profile, including choosing a name, and an avatar.
8 */ 8 */
9 9
10 /** @typedef {{url: string, label:string}} */ 10 /** @typedef {{url: string, label:string}} */
11 var AvatarIcon; 11 var AvatarIcon;
12 12
13 (function() { 13 (function() {
14 /** 14 /**
15 * Sentinel signed-in user's index value. 15 * Sentinel signed-in user's index value.
16 * @const {number} 16 * @const {number}
17 */ 17 */
18 var NO_USER_SELECTED = -1; 18 var NO_USER_SELECTED = -1;
19 19
20 Polymer({ 20 Polymer({
21 is: 'create-profile', 21 is: 'create-profile',
22 22
23 behaviors: [ 23 behaviors: [I18nBehavior, WebUIListenerBehavior],
24 I18nBehavior,
25 WebUIListenerBehavior
26 ],
27 24
28 properties: { 25 properties: {
29 /** 26 /**
30 * The current profile name. 27 * The current profile name.
31 * @private {string} 28 * @private {string}
32 */ 29 */
33 profileName_: { 30 profileName_: {type: String, value: ''},
34 type: String,
35 value: ''
36 },
37 31
38 /** 32 /**
39 * The list of available profile icon Urls and labels. 33 * The list of available profile icon Urls and labels.
40 * @private {!Array<!AvatarIcon>} 34 * @private {!Array<!AvatarIcon>}
41 */ 35 */
42 availableIcons_: { 36 availableIcons_: {
43 type: Array, 37 type: Array,
44 value: function() { return []; } 38 value: function() {
39 return [];
40 }
45 }, 41 },
46 42
47 /** 43 /**
48 * The currently selected profile icon URL. May be a data URL. 44 * The currently selected profile icon URL. May be a data URL.
49 * @private {string} 45 * @private {string}
50 */ 46 */
51 profileIconUrl_: { 47 profileIconUrl_: {type: String, value: ''},
52 type: String,
53 value: ''
54 },
55 48
56 /** 49 /**
57 * True if the existing supervised users are being loaded. 50 * True if the existing supervised users are being loaded.
58 * @private {boolean} 51 * @private {boolean}
59 */ 52 */
60 loadingSupervisedUsers_: { 53 loadingSupervisedUsers_: {type: Boolean, value: false},
61 type: Boolean,
62 value: false
63 },
64 54
65 /** 55 /**
66 * True if a profile is being created or imported. 56 * True if a profile is being created or imported.
67 * @private {boolean} 57 * @private {boolean}
68 */ 58 */
69 createInProgress_: { 59 createInProgress_: {type: Boolean, value: false},
70 type: Boolean,
71 value: false
72 },
73 60
74 /** 61 /**
75 * True if the error/warning message is displaying. 62 * True if the error/warning message is displaying.
76 * @private {boolean} 63 * @private {boolean}
77 */ 64 */
78 isMessageVisble_: { 65 isMessageVisble_: {type: Boolean, value: false},
79 type: Boolean,
80 value: false
81 },
82 66
83 /** 67 /**
84 * The current error/warning message. 68 * The current error/warning message.
85 * @private {string} 69 * @private {string}
86 */ 70 */
87 message_: { 71 message_: {type: String, value: ''},
88 type: String,
89 value: ''
90 },
91 72
92 /** 73 /**
93 * if true, a desktop shortcut will be created for the new profile. 74 * if true, a desktop shortcut will be created for the new profile.
94 * @private {boolean} 75 * @private {boolean}
95 */ 76 */
96 createShortcut_: { 77 createShortcut_: {type: Boolean, value: true},
97 type: Boolean,
98 value: true
99 },
100 78
101 /** 79 /**
102 * True if the new profile is a supervised profile. 80 * True if the new profile is a supervised profile.
103 * @private {boolean} 81 * @private {boolean}
104 */ 82 */
105 isSupervised_: { 83 isSupervised_: {type: Boolean, value: false},
106 type: Boolean,
107 value: false
108 },
109 84
110 /** 85 /**
111 * The list of usernames and profile paths for currently signed-in users. 86 * The list of usernames and profile paths for currently signed-in users.
112 * @private {!Array<!SignedInUser>} 87 * @private {!Array<!SignedInUser>}
113 */ 88 */
114 signedInUsers_: { 89 signedInUsers_: {
115 type: Array, 90 type: Array,
116 value: function() { return []; } 91 value: function() {
92 return [];
93 }
117 }, 94 },
118 95
119 /** 96 /**
120 * Index of the selected signed-in user. 97 * Index of the selected signed-in user.
121 * @private {number} 98 * @private {number}
122 */ 99 */
123 signedInUserIndex_: { 100 signedInUserIndex_: {type: Number, value: NO_USER_SELECTED},
124 type: Number,
125 value: NO_USER_SELECTED
126 },
127 101
128 /** @private {!signin.ProfileBrowserProxy} */ 102 /** @private {!signin.ProfileBrowserProxy} */
129 browserProxy_: Object, 103 browserProxy_: Object,
130 104
131 /** 105 /**
132 * True if the profile shortcuts feature is enabled. 106 * True if the profile shortcuts feature is enabled.
133 * @private 107 * @private
134 */ 108 */
135 isProfileShortcutsEnabled_: { 109 isProfileShortcutsEnabled_: {
136 type: Boolean, 110 type: Boolean,
137 value: function() { 111 value: function() {
138 return loadTimeData.getBoolean('profileShortcutsEnabled'); 112 return loadTimeData.getBoolean('profileShortcutsEnabled');
139 }, 113 },
140 readOnly: true 114 readOnly: true
141 }, 115 },
142 116
143 /** 117 /**
144 * True if the force sign in policy is enabled. 118 * True if the force sign in policy is enabled.
145 * @private {boolean} 119 * @private {boolean}
146 */ 120 */
147 isForceSigninEnabled_: { 121 isForceSigninEnabled_: {
148 type: Boolean, 122 type: Boolean,
149 value: function() { 123 value: function() {
150 return loadTimeData.getBoolean('isForceSigninEnabled'); 124 return loadTimeData.getBoolean('isForceSigninEnabled');
151 }, 125 },
152 } 126 }
153 }, 127 },
154 128
155 listeners: { 129 listeners:
156 'tap': 'onTap_', 130 {'tap': 'onTap_', 'importUserPopup.import': 'onImportUserPopupImport_'},
157 'importUserPopup.import': 'onImportUserPopupImport_'
158 },
159 131
160 /** @override */ 132 /** @override */
161 created: function() { 133 created: function() {
162 this.browserProxy_ = signin.ProfileBrowserProxyImpl.getInstance(); 134 this.browserProxy_ = signin.ProfileBrowserProxyImpl.getInstance();
163 }, 135 },
164 136
165 /** @override */ 137 /** @override */
166 ready: function() { 138 ready: function() {
167 this.addWebUIListener( 139 this.addWebUIListener(
168 'create-profile-success', this.handleSuccess_.bind(this)); 140 'create-profile-success', this.handleSuccess_.bind(this));
169 this.addWebUIListener( 141 this.addWebUIListener(
170 'create-profile-warning', this.handleMessage_.bind(this)); 142 'create-profile-warning', this.handleMessage_.bind(this));
171 this.addWebUIListener( 143 this.addWebUIListener(
172 'create-profile-error', this.handleMessage_.bind(this)); 144 'create-profile-error', this.handleMessage_.bind(this));
173 this.addWebUIListener( 145 this.addWebUIListener(
174 'profile-icons-received', this.handleProfileIcons_.bind(this)); 146 'profile-icons-received', this.handleProfileIcons_.bind(this));
175 this.addWebUIListener( 147 this.addWebUIListener(
176 'profile-defaults-received', this.handleProfileDefaults_.bind(this)); 148 'profile-defaults-received', this.handleProfileDefaults_.bind(this));
177 this.addWebUIListener( 149 this.addWebUIListener(
178 'signedin-users-received', this.handleSignedInUsers_.bind(this)); 150 'signedin-users-received', this.handleSignedInUsers_.bind(this));
179 151
180 this.browserProxy_.getAvailableIcons(); 152 this.browserProxy_.getAvailableIcons();
181 this.browserProxy_.getSignedInUsers(); 153 this.browserProxy_.getSignedInUsers();
182 }, 154 },
183 155
184 /** @override */ 156 /** @override */
185 attached: function() { 157 attached: function() {
186 this.$.nameInput.focus(); 158 this.$.nameInput.focus();
187 }, 159 },
188 160
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
264 onImportUserTap_: function(event) { 236 onImportUserTap_: function(event) {
265 if (this.signedInUserIndex_ == NO_USER_SELECTED) { 237 if (this.signedInUserIndex_ == NO_USER_SELECTED) {
266 // A custodian must be selected. 238 // A custodian must be selected.
267 this.handleMessage_( 239 this.handleMessage_(
268 this.i18nAdvanced('custodianAccountNotSelectedError')); 240 this.i18nAdvanced('custodianAccountNotSelectedError'));
269 } else { 241 } else {
270 var signedInUser = this.signedInUser_(this.signedInUserIndex_); 242 var signedInUser = this.signedInUser_(this.signedInUserIndex_);
271 this.hideMessage_(); 243 this.hideMessage_();
272 this.loadingSupervisedUsers_ = true; 244 this.loadingSupervisedUsers_ = true;
273 this.browserProxy_.getExistingSupervisedUsers(signedInUser.profilePath) 245 this.browserProxy_.getExistingSupervisedUsers(signedInUser.profilePath)
274 .then(this.showImportSupervisedUserPopup_.bind(this), 246 .then(
275 this.handleMessage_.bind(this)); 247 this.showImportSupervisedUserPopup_.bind(this),
248 this.handleMessage_.bind(this));
276 } 249 }
277 }, 250 },
278 251
279 /** 252 /**
280 * Handler for the 'Save' button tap event. 253 * Handler for the 'Save' button tap event.
281 * @param {!Event} event 254 * @param {!Event} event
282 * @private 255 * @private
283 */ 256 */
284 onSaveTap_: function(event) { 257 onSaveTap_: function(event) {
285 if (!this.isSupervised_) { 258 if (!this.isSupervised_) {
286 // The new profile is not supervised. Go ahead and create it. 259 // The new profile is not supervised. Go ahead and create it.
287 this.createProfile_(); 260 this.createProfile_();
288 } else if (this.signedInUserIndex_ == NO_USER_SELECTED) { 261 } else if (this.signedInUserIndex_ == NO_USER_SELECTED) {
289 // If the new profile is supervised, a custodian must be selected. 262 // If the new profile is supervised, a custodian must be selected.
290 this.handleMessage_( 263 this.handleMessage_(
291 this.i18nAdvanced('custodianAccountNotSelectedError')); 264 this.i18nAdvanced('custodianAccountNotSelectedError'));
292 } else { 265 } else {
293 var signedInUser = this.signedInUser_(this.signedInUserIndex_); 266 var signedInUser = this.signedInUser_(this.signedInUserIndex_);
294 this.hideMessage_(); 267 this.hideMessage_();
295 this.loadingSupervisedUsers_ = true; 268 this.loadingSupervisedUsers_ = true;
296 this.browserProxy_.getExistingSupervisedUsers(signedInUser.profilePath) 269 this.browserProxy_.getExistingSupervisedUsers(signedInUser.profilePath)
297 .then(this.createProfileIfValidSupervisedUser_.bind(this), 270 .then(
298 this.handleMessage_.bind(this)); 271 this.createProfileIfValidSupervisedUser_.bind(this),
272 this.handleMessage_.bind(this));
299 } 273 }
300 }, 274 },
301 275
302 /** 276 /**
303 * Displays the import supervised user popup or an error message if there are 277 * Displays the import supervised user popup or an error message if there are
304 * no existing supervised users. 278 * no existing supervised users.
305 * @param {!Array<!SupervisedUser>} supervisedUsers The list of existing 279 * @param {!Array<!SupervisedUser>} supervisedUsers The list of existing
306 * supervised users. 280 * supervised users.
307 * @private 281 * @private
308 */ 282 */
309 showImportSupervisedUserPopup_: function(supervisedUsers) { 283 showImportSupervisedUserPopup_: function(supervisedUsers) {
310 this.loadingSupervisedUsers_ = false; 284 this.loadingSupervisedUsers_ = false;
311 if (supervisedUsers.length > 0) { 285 if (supervisedUsers.length > 0) {
312 this.$.importUserPopup.show(this.signedInUser_(this.signedInUserIndex_), 286 this.$.importUserPopup.show(
313 supervisedUsers); 287 this.signedInUser_(this.signedInUserIndex_), supervisedUsers);
314 } else { 288 } else {
315 this.handleMessage_(this.i18nAdvanced('noSupervisedUserImportText')); 289 this.handleMessage_(this.i18nAdvanced('noSupervisedUserImportText'));
316 } 290 }
317 }, 291 },
318 292
319 /** 293 /**
320 * Checks if the entered name matches name of an existing supervised user. 294 * Checks if the entered name matches name of an existing supervised user.
321 * If yes, the user is prompted to import the existing supervised user. 295 * If yes, the user is prompted to import the existing supervised user.
322 * If no, the new supervised profile gets created. 296 * If no, the new supervised profile gets created.
323 * @param {!Array<!SupervisedUser>} supervisedUsers The list of existing 297 * @param {!Array<!SupervisedUser>} supervisedUsers The list of existing
324 * supervised users. 298 * supervised users.
325 * @private 299 * @private
326 */ 300 */
327 createProfileIfValidSupervisedUser_: function(supervisedUsers) { 301 createProfileIfValidSupervisedUser_: function(supervisedUsers) {
328 for (var i = 0; i < supervisedUsers.length; ++i) { 302 for (var i = 0; i < supervisedUsers.length; ++i) {
329 if (supervisedUsers[i].name != this.profileName_) 303 if (supervisedUsers[i].name != this.profileName_)
330 continue; 304 continue;
331 // Check if another supervised user also exists with that name. 305 // Check if another supervised user also exists with that name.
332 var nameIsUnique = true; 306 var nameIsUnique = true;
333 // Handling the case when multiple supervised users with the same 307 // Handling the case when multiple supervised users with the same
334 // name exist, but not all of them are on the device. 308 // name exist, but not all of them are on the device.
335 // If at least one is not imported, we want to offer that 309 // If at least one is not imported, we want to offer that
336 // option to the user. This could happen due to a bug that allowed 310 // option to the user. This could happen due to a bug that allowed
337 // creating SUs with the same name (https://crbug.com/557445). 311 // creating SUs with the same name (https://crbug.com/557445).
338 var allOnCurrentDevice = supervisedUsers[i].onCurrentDevice; 312 var allOnCurrentDevice = supervisedUsers[i].onCurrentDevice;
339 for (var j = i + 1; j < supervisedUsers.length; ++j) { 313 for (var j = i + 1; j < supervisedUsers.length; ++j) {
340 if (supervisedUsers[j].name == this.profileName_) { 314 if (supervisedUsers[j].name == this.profileName_) {
341 nameIsUnique = false; 315 nameIsUnique = false;
342 allOnCurrentDevice = allOnCurrentDevice && 316 allOnCurrentDevice =
343 supervisedUsers[j].onCurrentDevice; 317 allOnCurrentDevice && supervisedUsers[j].onCurrentDevice;
344 } 318 }
345 } 319 }
346 320
347 var opts = { 321 var opts = {
348 'substitutions': 322 'substitutions':
349 [HTMLEscape(elide(this.profileName_, /* maxLength */ 50))], 323 [HTMLEscape(elide(this.profileName_, /* maxLength */ 50))],
350 'attrs': { 324 'attrs': {
351 'id': function(node, value) { 325 'id': function(node, value) {
352 return node.tagName == 'A'; 326 return node.tagName == 'A';
353 }, 327 },
354 'is': function(node, value) { 328 'is': function(node, value) {
355 return node.tagName == 'A' && value == 'action-link'; 329 return node.tagName == 'A' && value == 'action-link';
356 }, 330 },
357 'role': function(node, value) { 331 'role': function(node, value) {
358 return node.tagName == 'A' && value == 'link'; 332 return node.tagName == 'A' && value == 'link';
359 }, 333 },
(...skipping 22 matching lines...) Expand all
382 * @private 356 * @private
383 */ 357 */
384 createProfile_: function() { 358 createProfile_: function() {
385 var custodianProfilePath = ''; 359 var custodianProfilePath = '';
386 if (this.signedInUserIndex_ != NO_USER_SELECTED) { 360 if (this.signedInUserIndex_ != NO_USER_SELECTED) {
387 custodianProfilePath = 361 custodianProfilePath =
388 this.signedInUser_(this.signedInUserIndex_).profilePath; 362 this.signedInUser_(this.signedInUserIndex_).profilePath;
389 } 363 }
390 this.hideMessage_(); 364 this.hideMessage_();
391 this.createInProgress_ = true; 365 this.createInProgress_ = true;
392 var createShortcut = this.isProfileShortcutsEnabled_ && 366 var createShortcut =
393 this.createShortcut_; 367 this.isProfileShortcutsEnabled_ && this.createShortcut_;
394 this.browserProxy_.createProfile( 368 this.browserProxy_.createProfile(
395 this.profileName_, this.profileIconUrl_, createShortcut, 369 this.profileName_, this.profileIconUrl_, createShortcut,
396 this.isSupervised_, '', custodianProfilePath); 370 this.isSupervised_, '', custodianProfilePath);
397 }, 371 },
398 372
399 /** 373 /**
400 * Handler for the 'import' event fired by #importUserPopup once a supervised 374 * Handler for the 'import' event fired by #importUserPopup once a supervised
401 * user is selected to be imported and the popup closes. 375 * user is selected to be imported and the popup closes.
402 * @param {!{detail: {supervisedUser: !SupervisedUser, 376 * @param {!{detail: {supervisedUser: !SupervisedUser,
403 * signedInUser: !SignedInUser}}} event 377 * signedInUser: !SignedInUser}}} event
(...skipping 28 matching lines...) Expand all
432 }, 406 },
433 407
434 /** 408 /**
435 * Handles profile create/import success message pushed by the browser. 409 * Handles profile create/import success message pushed by the browser.
436 * @param {!ProfileInfo} profileInfo Details of the created/imported profile. 410 * @param {!ProfileInfo} profileInfo Details of the created/imported profile.
437 * @private 411 * @private
438 */ 412 */
439 handleSuccess_: function(profileInfo) { 413 handleSuccess_: function(profileInfo) {
440 this.createInProgress_ = false; 414 this.createInProgress_ = false;
441 if (profileInfo.showConfirmation) { 415 if (profileInfo.showConfirmation) {
442 this.fire('change-page', {page: 'supervised-create-confirm-page', 416 this.fire(
443 data: profileInfo}); 417 'change-page',
418 {page: 'supervised-create-confirm-page', data: profileInfo});
444 } else { 419 } else {
445 this.fire('change-page', {page: 'user-pods-page'}); 420 this.fire('change-page', {page: 'user-pods-page'});
446 } 421 }
447 }, 422 },
448 423
449 /** 424 /**
450 * Hides the warning/error message. 425 * Hides the warning/error message.
451 * @private 426 * @private
452 */ 427 */
453 hideMessage_: function() { 428 hideMessage_: function() {
(...skipping 15 matching lines...) Expand all
469 444
470 /** 445 /**
471 * Returns a translated message that contains link elements with the 'id' 446 * Returns a translated message that contains link elements with the 'id'
472 * attribute. 447 * attribute.
473 * @param {string} id The ID of the string to translate. 448 * @param {string} id The ID of the string to translate.
474 * @private 449 * @private
475 */ 450 */
476 i18nAllowIDAttr_: function(id) { 451 i18nAllowIDAttr_: function(id) {
477 var opts = { 452 var opts = {
478 'attrs': { 453 'attrs': {
479 'id' : function(node, value) { 454 'id': function(node, value) {
480 return node.tagName == 'A'; 455 return node.tagName == 'A';
481 } 456 }
482 } 457 }
483 }; 458 };
484 459
485 return this.i18nAdvanced(id, opts); 460 return this.i18nAdvanced(id, opts);
486 }, 461 },
487 462
488 /** 463 /**
489 * Computed binding determining which profile icon button is toggled on. 464 * Computed binding determining which profile icon button is toggled on.
(...skipping 18 matching lines...) Expand all
508 }, 483 },
509 484
510 /** 485 /**
511 * Computed binding determining whether 'Save' button is disabled. 486 * Computed binding determining whether 'Save' button is disabled.
512 * @param {boolean} createInProgress Is create in progress? 487 * @param {boolean} createInProgress Is create in progress?
513 * @param {boolean} loadingSupervisedUsers Are supervised users being loaded? 488 * @param {boolean} loadingSupervisedUsers Are supervised users being loaded?
514 * @param {string} profileName Profile Name. 489 * @param {string} profileName Profile Name.
515 * @return {boolean} 490 * @return {boolean}
516 * @private 491 * @private
517 */ 492 */
518 isSaveDisabled_: function(createInProgress, 493 isSaveDisabled_: function(
519 loadingSupervisedUsers, 494 createInProgress, loadingSupervisedUsers, profileName) {
520 profileName) {
521 // TODO(mahmadi): Figure out a way to add 'paper-input-extracted' as a 495 // TODO(mahmadi): Figure out a way to add 'paper-input-extracted' as a
522 // dependency and cast to PaperInputElement instead. 496 // dependency and cast to PaperInputElement instead.
523 /** @type {{validate: function():boolean}} */ 497 /** @type {{validate: function():boolean}} */
524 var nameInput = this.$.nameInput; 498 var nameInput = this.$.nameInput;
525 return createInProgress || loadingSupervisedUsers || !profileName || 499 return createInProgress || loadingSupervisedUsers || !profileName ||
526 !nameInput.validate(); 500 !nameInput.validate();
527 }, 501 },
528 502
529 /** 503 /**
530 * Returns True if the import existing supervised user link should be hidden. 504 * Returns True if the import existing supervised user link should be hidden.
531 * @param {boolean} createInProgress True if create/import is in progress. 505 * @param {boolean} createInProgress True if create/import is in progress.
532 * @param {boolean} loadingSupervisedUsers True if supervised users are being 506 * @param {boolean} loadingSupervisedUsers True if supervised users are being
533 * loaded. 507 * loaded.
534 * @param {number} signedInUserIndex Index of the selected signed-in user. 508 * @param {number} signedInUserIndex Index of the selected signed-in user.
535 * @return {boolean} 509 * @return {boolean}
536 * @private 510 * @private
537 */ 511 */
538 isImportUserLinkHidden_: function(createInProgress, 512 isImportUserLinkHidden_: function(
539 loadingSupervisedUsers, 513 createInProgress, loadingSupervisedUsers, signedInUserIndex) {
540 signedInUserIndex) {
541 return createInProgress || loadingSupervisedUsers || 514 return createInProgress || loadingSupervisedUsers ||
542 !this.signedInUser_(signedInUserIndex); 515 !this.signedInUser_(signedInUserIndex);
543 }, 516 },
544 517
545 /** 518 /**
546 * Computed binding that returns True if there are any signed-in users. 519 * Computed binding that returns True if there are any signed-in users.
547 * @param {!Array<!SignedInUser>} signedInUsers signed-in users. 520 * @param {!Array<!SignedInUser>} signedInUsers signed-in users.
548 * @return {boolean} 521 * @return {boolean}
549 * @private 522 * @private
550 */ 523 */
551 isSignedIn_: function(signedInUsers) { 524 isSignedIn_: function(signedInUsers) {
552 return signedInUsers.length > 0; 525 return signedInUsers.length > 0;
553 } 526 }
554 }); 527 });
555 }()); 528 }());
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698