View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.cms.ui.workbench.internal.useradmin.parts;
17  
18  import static org.argeo.cms.util.UserAdminUtils.getProperty;
19  import static org.argeo.naming.LdapAttrs.cn;
20  import static org.argeo.naming.LdapAttrs.givenName;
21  import static org.argeo.naming.LdapAttrs.mail;
22  import static org.argeo.naming.LdapAttrs.sn;
23  import static org.argeo.naming.LdapAttrs.uid;
24  
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  import org.argeo.cms.ArgeoNames;
30  import org.argeo.cms.auth.CurrentUser;
31  import org.argeo.cms.ui.workbench.CmsWorkbenchStyles;
32  import org.argeo.cms.ui.workbench.internal.useradmin.SecurityAdminImages;
33  import org.argeo.cms.ui.workbench.internal.useradmin.UserAdminWrapper;
34  import org.argeo.cms.ui.workbench.internal.useradmin.parts.UserEditor.GroupChangeListener;
35  import org.argeo.cms.ui.workbench.internal.useradmin.parts.UserEditor.MainInfoListener;
36  import org.argeo.cms.ui.workbench.internal.useradmin.providers.CommonNameLP;
37  import org.argeo.cms.ui.workbench.internal.useradmin.providers.DomainNameLP;
38  import org.argeo.cms.ui.workbench.internal.useradmin.providers.RoleIconLP;
39  import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserFilter;
40  import org.argeo.cms.ui.workbench.internal.useradmin.providers.UserTableDefaultDClickListener;
41  import org.argeo.cms.util.CmsUtils;
42  import org.argeo.cms.util.UserAdminUtils;
43  import org.argeo.eclipse.ui.ColumnDefinition;
44  import org.argeo.eclipse.ui.EclipseUiUtils;
45  import org.argeo.eclipse.ui.parts.LdifUsersTable;
46  import org.argeo.naming.LdapAttrs;
47  import org.argeo.node.NodeConstants;
48  import org.eclipse.jface.action.Action;
49  import org.eclipse.jface.action.ToolBarManager;
50  import org.eclipse.jface.dialogs.MessageDialog;
51  import org.eclipse.jface.dialogs.TrayDialog;
52  import org.eclipse.jface.resource.ImageDescriptor;
53  import org.eclipse.jface.viewers.ISelection;
54  import org.eclipse.jface.viewers.IStructuredSelection;
55  import org.eclipse.jface.viewers.TableViewer;
56  import org.eclipse.jface.viewers.Viewer;
57  import org.eclipse.jface.viewers.ViewerDropAdapter;
58  import org.eclipse.swt.SWT;
59  import org.eclipse.swt.dnd.DND;
60  import org.eclipse.swt.dnd.DropTargetEvent;
61  import org.eclipse.swt.dnd.TextTransfer;
62  import org.eclipse.swt.dnd.Transfer;
63  import org.eclipse.swt.dnd.TransferData;
64  import org.eclipse.swt.events.DisposeEvent;
65  import org.eclipse.swt.events.DisposeListener;
66  import org.eclipse.swt.events.ModifyEvent;
67  import org.eclipse.swt.events.ModifyListener;
68  import org.eclipse.swt.events.SelectionAdapter;
69  import org.eclipse.swt.events.SelectionEvent;
70  import org.eclipse.swt.graphics.Cursor;
71  import org.eclipse.swt.layout.GridData;
72  import org.eclipse.swt.layout.GridLayout;
73  import org.eclipse.swt.widgets.Button;
74  import org.eclipse.swt.widgets.Composite;
75  import org.eclipse.swt.widgets.Control;
76  import org.eclipse.swt.widgets.Display;
77  import org.eclipse.swt.widgets.Label;
78  import org.eclipse.swt.widgets.Link;
79  import org.eclipse.swt.widgets.Shell;
80  import org.eclipse.swt.widgets.Text;
81  import org.eclipse.swt.widgets.ToolBar;
82  import org.eclipse.ui.forms.AbstractFormPart;
83  import org.eclipse.ui.forms.IManagedForm;
84  import org.eclipse.ui.forms.SectionPart;
85  import org.eclipse.ui.forms.editor.FormEditor;
86  import org.eclipse.ui.forms.editor.FormPage;
87  import org.eclipse.ui.forms.widgets.FormToolkit;
88  import org.eclipse.ui.forms.widgets.ScrolledForm;
89  import org.eclipse.ui.forms.widgets.Section;
90  import org.osgi.service.useradmin.Group;
91  import org.osgi.service.useradmin.Role;
92  import org.osgi.service.useradmin.User;
93  import org.osgi.service.useradmin.UserAdmin;
94  import org.osgi.service.useradmin.UserAdminEvent;
95  
96  /** Display/edit the properties of a given user */
97  public class UserMainPage extends FormPage implements ArgeoNames {
98  	final static String ID = "UserEditor.mainPage";
99  
100 	private final UserEditor editor;
101 	private UserAdminWrapper userAdminWrapper;
102 
103 	// Local configuration
104 	private final int PRE_TITLE_INDENT = 10;
105 
106 	public UserMainPage(FormEditor editor, UserAdminWrapper userAdminWrapper) {
107 		super(editor, ID, "Main");
108 		this.editor = (UserEditor) editor;
109 		this.userAdminWrapper = userAdminWrapper;
110 	}
111 
112 	protected void createFormContent(final IManagedForm mf) {
113 		ScrolledForm form = mf.getForm();
114 		Composite body = form.getBody();
115 		GridLayout mainLayout = new GridLayout();
116 		// mainLayout.marginRight = 10;
117 		body.setLayout(mainLayout);
118 		User user = editor.getDisplayedUser();
119 		appendOverviewPart(body, user);
120 		// Remove to ability to force the password for his own user. The user
121 		// must then use the change pwd feature
122 		appendMemberOfPart(body, user);
123 	}
124 
125 	/** Creates the general section */
126 	private void appendOverviewPart(final Composite parent, final User user) {
127 		FormToolkit tk = getManagedForm().getToolkit();
128 
129 		Section section = tk.createSection(parent, SWT.NO_FOCUS);
130 		GridData gd = EclipseUiUtils.fillWidth();
131 		// gd.verticalAlignment = PRE_TITLE_INDENT;
132 		section.setLayoutData(gd);
133 		Composite body = tk.createComposite(section, SWT.WRAP);
134 		body.setLayoutData(EclipseUiUtils.fillAll());
135 		section.setClient(body);
136 		// body.setLayout(new GridLayout(6, false));
137 		body.setLayout(new GridLayout(2, false));
138 
139 		Text commonName = createReadOnlyLT(tk, body, "Name", getProperty(user, cn));
140 		Text distinguishedName = createReadOnlyLT(tk, body, "Login", getProperty(user, uid));
141 		Text firstName = createLT(tk, body, "First name", getProperty(user, givenName));
142 		Text lastName = createLT(tk, body, "Last name", getProperty(user, sn));
143 		Text email = createLT(tk, body, "Email", getProperty(user, mail));
144 
145 		Link resetPwdLk = new Link(body, SWT.NONE);
146 		if (!UserAdminUtils.isCurrentUser(user)) {
147 			resetPwdLk.setText("<a>Reset password</a>");
148 		}
149 		resetPwdLk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
150 
151 		// create form part (controller)
152 		AbstractFormPart part = new SectionPart((Section) body.getParent()) {
153 			private MainInfoListener listener;
154 
155 			@Override
156 			public void initialize(IManagedForm form) {
157 				super.initialize(form);
158 				listener = editor.new MainInfoListener(parent.getDisplay(), this);
159 				userAdminWrapper.addListener(listener);
160 			}
161 
162 			@Override
163 			public void dispose() {
164 				userAdminWrapper.removeListener(listener);
165 				super.dispose();
166 			}
167 
168 			@SuppressWarnings("unchecked")
169 			public void commit(boolean onSave) {
170 				// TODO Sanity checks (mail validity...)
171 				user.getProperties().put(LdapAttrs.givenName.name(), firstName.getText());
172 				user.getProperties().put(LdapAttrs.sn.name(), lastName.getText());
173 				user.getProperties().put(LdapAttrs.cn.name(), commonName.getText());
174 				user.getProperties().put(LdapAttrs.mail.name(), email.getText());
175 				super.commit(onSave);
176 			}
177 
178 			@Override
179 			public void refresh() {
180 				distinguishedName.setText(UserAdminUtils.getProperty(user, LdapAttrs.uid.name()));
181 				commonName.setText(UserAdminUtils.getProperty(user, LdapAttrs.cn.name()));
182 				firstName.setText(UserAdminUtils.getProperty(user, LdapAttrs.givenName.name()));
183 				lastName.setText(UserAdminUtils.getProperty(user, LdapAttrs.sn.name()));
184 				email.setText(UserAdminUtils.getProperty(user, LdapAttrs.mail.name()));
185 				refreshFormTitle(user);
186 				super.refresh();
187 			}
188 		};
189 
190 		// Improve this: automatically generate CN when first or last name
191 		// changes
192 		ModifyListener cnML = new ModifyListener() {
193 			private static final long serialVersionUID = 4298649222869835486L;
194 
195 			@Override
196 			public void modifyText(ModifyEvent event) {
197 				String first = firstName.getText();
198 				String last = lastName.getText();
199 				String cn = first.trim() + " " + last.trim() + " ";
200 				cn = cn.trim();
201 				commonName.setText(cn);
202 				// getManagedForm().getForm().setText(cn);
203 				editor.updateEditorTitle(cn);
204 			}
205 		};
206 		firstName.addModifyListener(cnML);
207 		lastName.addModifyListener(cnML);
208 
209 		ModifyListener defaultListener = editor.new FormPartML(part);
210 		firstName.addModifyListener(defaultListener);
211 		lastName.addModifyListener(defaultListener);
212 		email.addModifyListener(defaultListener);
213 
214 		if (!UserAdminUtils.isCurrentUser(user))
215 			resetPwdLk.addSelectionListener(new SelectionAdapter() {
216 				private static final long serialVersionUID = 5881800534589073787L;
217 
218 				@Override
219 				public void widgetSelected(SelectionEvent e) {
220 					new ChangePasswordDialog(tk, user, "Reset password").open();
221 				}
222 			});
223 
224 		getManagedForm().addPart(part);
225 	}
226 
227 	private class ChangePasswordDialog extends TrayDialog {
228 		private static final long serialVersionUID = 2843538207460082349L;
229 
230 		private User user;
231 		private Text password1;
232 		private Text password2;
233 		private String title;
234 		private FormToolkit tk;
235 
236 		public ChangePasswordDialog(FormToolkit tk, User user, String title) {
237 			super(Display.getDefault().getActiveShell());
238 			this.tk = tk;
239 			this.user = user;
240 			this.title = title;
241 		}
242 
243 		protected Control createDialogArea(Composite parent) {
244 			Composite dialogarea = (Composite) super.createDialogArea(parent);
245 			dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
246 			Composite body = new Composite(dialogarea, SWT.NO_FOCUS);
247 			body.setLayoutData(EclipseUiUtils.fillAll());
248 			GridLayout layout = new GridLayout(2, false);
249 			body.setLayout(layout);
250 
251 			password1 = createLP(tk, body, "New password", "");
252 			password2 = createLP(tk, body, "Repeat password", "");
253 			parent.pack();
254 			return body;
255 		}
256 
257 		@SuppressWarnings("unchecked")
258 		@Override
259 		protected void okPressed() {
260 			String msg = null;
261 
262 			if (password1.getText().equals(""))
263 				msg = "Password cannot be empty";
264 			else if (password1.getText().equals(password2.getText())) {
265 				char[] newPassword = password1.getText().toCharArray();
266 				// userAdminWrapper.beginTransactionIfNeeded();
267 				userAdminWrapper.beginTransactionIfNeeded();
268 				user.getCredentials().put(null, newPassword);
269 				userAdminWrapper.commitOrNotifyTransactionStateChange();
270 				super.okPressed();
271 			} else {
272 				msg = "Passwords are not equals";
273 			}
274 
275 			if (EclipseUiUtils.notEmpty(msg))
276 				MessageDialog.openError(getParentShell(), "Cannot reset pasword", msg);
277 		}
278 
279 		protected void configureShell(Shell shell) {
280 			super.configureShell(shell);
281 			shell.setText(title);
282 		}
283 	}
284 
285 	private LdifUsersTable appendMemberOfPart(final Composite parent, User user) {
286 		FormToolkit tk = getManagedForm().getToolkit();
287 		Section section = addSection(tk, parent, "Roles");
288 		Composite body = (Composite) section.getClient();
289 		body.setLayout(EclipseUiUtils.noSpaceGridLayout());
290 
291 		// boolean isAdmin = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
292 
293 		// Displayed columns
294 		List<ColumnDefinition> columnDefs = new ArrayList<ColumnDefinition>();
295 		columnDefs.add(new ColumnDefinition(new RoleIconLP(), "", 0, 24));
296 		columnDefs.add(new ColumnDefinition(new CommonNameLP(), "Name", 150));
297 		columnDefs.add(new ColumnDefinition(new DomainNameLP(), "Domain", 100));
298 		// Only show technical DN to administrators
299 		// if (isAdmin)
300 		// columnDefs.add(new ColumnDefinition(new UserNameLP(), "Distinguished Name",
301 		// 300));
302 
303 		// Create and configure the table
304 		final LdifUsersTable userViewerCmp = new MyUserTableViewer(body, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, user);
305 
306 		userViewerCmp.setColumnDefinitions(columnDefs);
307 		// if (isAdmin)
308 		// userViewerCmp.populateWithStaticFilters(false, false);
309 		// else
310 		userViewerCmp.populate(true, false);
311 		GridData gd = EclipseUiUtils.fillAll();
312 		gd.heightHint = 500;
313 		userViewerCmp.setLayoutData(gd);
314 
315 		// Controllers
316 		TableViewer userViewer = userViewerCmp.getTableViewer();
317 		userViewer.addDoubleClickListener(new UserTableDefaultDClickListener());
318 		int operations = DND.DROP_COPY | DND.DROP_MOVE;
319 		Transfer[] tt = new Transfer[] { TextTransfer.getInstance() };
320 		GroupDropListener dropL = new GroupDropListener(userAdminWrapper, userViewer, user);
321 		userViewer.addDropSupport(operations, tt, dropL);
322 
323 		SectionPart part = new SectionPart((Section) body.getParent()) {
324 
325 			private GroupChangeListener listener;
326 
327 			@Override
328 			public void initialize(IManagedForm form) {
329 				super.initialize(form);
330 				listener = editor.new GroupChangeListener(parent.getDisplay(), this);
331 				userAdminWrapper.addListener(listener);
332 			}
333 
334 			public void commit(boolean onSave) {
335 				super.commit(onSave);
336 			}
337 
338 			@Override
339 			public void dispose() {
340 				userAdminWrapper.removeListener(listener);
341 				super.dispose();
342 			}
343 
344 			@Override
345 			public void refresh() {
346 				userViewerCmp.refresh();
347 				super.refresh();
348 			}
349 		};
350 		getManagedForm().addPart(part);
351 		addRemoveAbitily(part, userViewer, user);
352 		return userViewerCmp;
353 	}
354 
355 	private class MyUserTableViewer extends LdifUsersTable {
356 		private static final long serialVersionUID = 2653790051461237329L;
357 
358 		private Button showSystemRoleBtn;
359 
360 		private final User user;
361 		private final UserFilter userFilter;
362 
363 		public MyUserTableViewer(Composite parent, int style, User user) {
364 			super(parent, style, true);
365 			this.user = user;
366 			userFilter = new UserFilter();
367 		}
368 
369 		protected void populateStaticFilters(Composite staticFilterCmp) {
370 			staticFilterCmp.setLayout(new GridLayout());
371 			showSystemRoleBtn = new Button(staticFilterCmp, SWT.CHECK);
372 			showSystemRoleBtn.setText("Show system roles");
373 			boolean showSysRole = CurrentUser.isInRole(NodeConstants.ROLE_ADMIN);
374 			showSystemRoleBtn.setSelection(showSysRole);
375 			userFilter.setShowSystemRole(showSysRole);
376 			showSystemRoleBtn.addSelectionListener(new SelectionAdapter() {
377 				private static final long serialVersionUID = -7033424592697691676L;
378 
379 				@Override
380 				public void widgetSelected(SelectionEvent e) {
381 					userFilter.setShowSystemRole(showSystemRoleBtn.getSelection());
382 					refresh();
383 				}
384 			});
385 		}
386 
387 		@Override
388 		protected List<User> listFilteredElements(String filter) {
389 			List<User> users = (List<User>) editor.getFlatGroups(null);
390 			List<User> filteredUsers = new ArrayList<User>();
391 			if (users.contains(user))
392 				users.remove(user);
393 			userFilter.setSearchText(filter);
394 			for (User user : users)
395 				if (userFilter.select(null, null, user))
396 					filteredUsers.add(user);
397 			return filteredUsers;
398 		}
399 	}
400 
401 	private void addRemoveAbitily(SectionPart sectionPart, TableViewer userViewer, User user) {
402 		Section section = sectionPart.getSection();
403 		ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
404 		ToolBar toolbar = toolBarManager.createControl(section);
405 		final Cursor handCursor = new Cursor(section.getDisplay(), SWT.CURSOR_HAND);
406 		toolbar.setCursor(handCursor);
407 		toolbar.addDisposeListener(new DisposeListener() {
408 			private static final long serialVersionUID = 3882131405820522925L;
409 
410 			public void widgetDisposed(DisposeEvent e) {
411 				if ((handCursor != null) && (handCursor.isDisposed() == false)) {
412 					handCursor.dispose();
413 				}
414 			}
415 		});
416 
417 		String tooltip = "Remove " + UserAdminUtils.getUserLocalId(user.getName()) + " from the below selected groups";
418 		Action action = new RemoveMembershipAction(userViewer, user, tooltip, SecurityAdminImages.ICON_REMOVE_DESC);
419 		toolBarManager.add(action);
420 		toolBarManager.update(true);
421 		section.setTextClient(toolbar);
422 	}
423 
424 	private class RemoveMembershipAction extends Action {
425 		private static final long serialVersionUID = -1337713097184522588L;
426 
427 		private final TableViewer userViewer;
428 		private final User user;
429 
430 		RemoveMembershipAction(TableViewer userViewer, User user, String name, ImageDescriptor img) {
431 			super(name, img);
432 			this.userViewer = userViewer;
433 			this.user = user;
434 		}
435 
436 		@Override
437 		public void run() {
438 			ISelection selection = userViewer.getSelection();
439 			if (selection.isEmpty())
440 				return;
441 
442 			@SuppressWarnings("unchecked")
443 			Iterator<Group> it = ((IStructuredSelection) selection).iterator();
444 			List<Group> groups = new ArrayList<Group>();
445 			while (it.hasNext()) {
446 				Group currGroup = it.next();
447 				groups.add(currGroup);
448 			}
449 
450 			userAdminWrapper.beginTransactionIfNeeded();
451 			for (Group group : groups) {
452 				group.removeMember(user);
453 			}
454 			userAdminWrapper.commitOrNotifyTransactionStateChange();
455 			for (Group group : groups) {
456 				userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
457 			}
458 		}
459 	}
460 
461 	/**
462 	 * Defines the table as being a potential target to add group memberships
463 	 * (roles) to this user
464 	 */
465 	private class GroupDropListener extends ViewerDropAdapter {
466 		private static final long serialVersionUID = 2893468717831451621L;
467 
468 		private final UserAdminWrapper myUserAdminWrapper;
469 		private final User myUser;
470 
471 		public GroupDropListener(UserAdminWrapper userAdminWrapper, Viewer userViewer, User user) {
472 			super(userViewer);
473 			this.myUserAdminWrapper = userAdminWrapper;
474 			this.myUser = user;
475 		}
476 
477 		@Override
478 		public boolean validateDrop(Object target, int operation, TransferData transferType) {
479 			// Target is always OK in a list only view
480 			// TODO check if not a string
481 			boolean validDrop = true;
482 			return validDrop;
483 		}
484 
485 		@Override
486 		public void drop(DropTargetEvent event) {
487 			String name = (String) event.data;
488 			UserAdmin myUserAdmin = myUserAdminWrapper.getUserAdmin();
489 			Role role = myUserAdmin.getRole(name);
490 			// TODO this check should be done before.
491 			if (role.getType() == Role.GROUP) {
492 				// TODO check if the user is already member of this group
493 
494 				myUserAdminWrapper.beginTransactionIfNeeded();
495 				Group group = (Group) role;
496 				group.addMember(myUser);
497 				userAdminWrapper.commitOrNotifyTransactionStateChange();
498 				myUserAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, group));
499 			}
500 			super.drop(event);
501 		}
502 
503 		@Override
504 		public boolean performDrop(Object data) {
505 			// userTableViewerCmp.refresh();
506 			return true;
507 		}
508 	}
509 
510 	// LOCAL HELPERS
511 	private void refreshFormTitle(User group) {
512 		// getManagedForm().getForm().setText(UserAdminUtils.getProperty(group,
513 		// LdapAttrs.cn.name()));
514 	}
515 
516 	/** Appends a section with a title */
517 	private Section addSection(FormToolkit tk, Composite parent, String title) {
518 		Section section = tk.createSection(parent, Section.TITLE_BAR);
519 		GridData gd = EclipseUiUtils.fillWidth();
520 		gd.verticalAlignment = PRE_TITLE_INDENT;
521 		section.setLayoutData(gd);
522 		section.setText(title);
523 		// section.getMenu().setVisible(true);
524 
525 		Composite body = tk.createComposite(section, SWT.WRAP);
526 		body.setLayoutData(EclipseUiUtils.fillAll());
527 		section.setClient(body);
528 
529 		return section;
530 	}
531 
532 	/** Creates label and multiline text. */
533 	Text createLMT(FormToolkit toolkit, Composite body, String label, String value) {
534 		Label lbl = toolkit.createLabel(body, label);
535 		lbl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
536 		Text text = toolkit.createText(body, value, SWT.BORDER | SWT.MULTI);
537 		text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
538 		return text;
539 	}
540 
541 	/** Creates label and password. */
542 	Text createLP(FormToolkit toolkit, Composite body, String label, String value) {
543 		Label lbl = toolkit.createLabel(body, label);
544 		lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
545 		Text text = toolkit.createText(body, value, SWT.BORDER | SWT.PASSWORD);
546 		text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
547 		return text;
548 	}
549 
550 	/** Creates label and text. */
551 	Text createLT(FormToolkit toolkit, Composite parent, String label, String value) {
552 		Label lbl = toolkit.createLabel(parent, label);
553 		lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
554 		lbl.setFont(EclipseUiUtils.getBoldFont(parent));
555 		Text text = toolkit.createText(parent, value, SWT.BORDER);
556 		text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
557 		CmsUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
558 		return text;
559 	}
560 
561 	Text createReadOnlyLT(FormToolkit toolkit, Composite parent, String label, String value) {
562 		Label lbl = toolkit.createLabel(parent, label);
563 		lbl.setLayoutData(new GridData(SWT.LEAD, SWT.CENTER, false, false));
564 		lbl.setFont(EclipseUiUtils.getBoldFont(parent));
565 		Text text = toolkit.createText(parent, value, SWT.NONE);
566 		text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
567 		text.setEditable(false);
568 		CmsUtils.style(text, CmsWorkbenchStyles.WORKBENCH_FORM_TEXT);
569 		return text;
570 	}
571 }