001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.apps.userdata; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsUser; 032import org.opencms.i18n.CmsEncoder; 033import org.opencms.jsp.userdata.I_CmsUserDataDomain; 034import org.opencms.jsp.userdata.Messages; 035import org.opencms.main.CmsException; 036import org.opencms.main.CmsLog; 037import org.opencms.main.OpenCms; 038import org.opencms.security.I_CmsPrincipal; 039import org.opencms.ui.A_CmsUI; 040import org.opencms.ui.CmsVaadinUtils; 041import org.opencms.ui.components.CmsBasicDialog; 042import org.opencms.ui.components.CmsBasicDialog.DialogWidth; 043import org.opencms.ui.components.CmsErrorDialog; 044import org.opencms.ui.components.OpenCmsTheme; 045import org.opencms.ui.components.editablegroup.CmsEditableGroup; 046import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect; 047import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect.WidgetType; 048import org.opencms.ui.dialogs.permissions.CmsPrincipalSelectDialog; 049import org.opencms.ui.report.CmsReportOverlay; 050import org.opencms.util.CmsFileUtil; 051import org.opencms.util.CmsStringUtil; 052 053import java.io.ByteArrayInputStream; 054import java.io.InputStream; 055import java.nio.charset.StandardCharsets; 056import java.util.ArrayList; 057import java.util.HashMap; 058import java.util.List; 059import java.util.concurrent.atomic.AtomicReference; 060import java.util.stream.Collectors; 061 062import org.apache.commons.logging.Log; 063 064import org.jsoup.Jsoup; 065import org.jsoup.nodes.Document; 066 067import com.vaadin.icons.VaadinIcons; 068import com.vaadin.server.FileDownloader; 069import com.vaadin.server.FontAwesome; 070import com.vaadin.server.Page; 071import com.vaadin.server.StreamResource; 072import com.vaadin.server.UserError; 073import com.vaadin.shared.ui.ContentMode; 074import com.vaadin.ui.Button; 075import com.vaadin.ui.FormLayout; 076import com.vaadin.ui.Label; 077import com.vaadin.ui.Notification; 078import com.vaadin.ui.TextField; 079import com.vaadin.ui.VerticalLayout; 080import com.vaadin.ui.Window; 081 082/** 083 * The GUI form for the user data app. 084 * 085 * <p>Generates a user data report either for a selected OpenCms user, or for an email address. 086 * The report is constructed by the I_CmsUserDataDomain plugins configured in opencms-system.xml. 087 */ 088public class CmsUserDataAppPanel extends VerticalLayout { 089 090 /** 091 * Helper class recording the status of a user data search operation. 092 */ 093 private class Status { 094 095 /** The m changed. */ 096 private volatile boolean m_changed; 097 098 /** The m exception. */ 099 private volatile Exception m_exception; 100 101 /** 102 * Gets the exception. 103 * 104 * @return the exception 105 */ 106 public Exception getException() { 107 108 return m_exception; 109 } 110 111 /** 112 * Checks if is changed. 113 * 114 * @return true, if is changed 115 */ 116 public boolean isChanged() { 117 118 return m_changed; 119 } 120 121 /** 122 * Sets the changed status. 123 * 124 * @param changed the new changed status 125 */ 126 public void setChanged(boolean changed) { 127 128 m_changed = changed; 129 } 130 131 /** 132 * Sets the exception. 133 * 134 * @param exception the new exception 135 */ 136 public void setException(Exception exception) { 137 138 m_exception = exception; 139 } 140 141 } 142 143 /** The serial version id. */ 144 private static final long serialVersionUID = 1L; 145 146 /** The logger instance for this class. */ 147 private static final Log LOG = CmsLog.getLog(CmsUserDataAppPanel.class); 148 149 /** CSS class for the container for the user data HTML. */ 150 public static final String O_USERDATA_CONTAINER = "o-userdata-container"; 151 152 /** The field for entering the email address. */ 153 protected TextField m_email; 154 155 /** Contains the (dynamic) additional text filter fields. */ 156 protected FormLayout m_filters; 157 158 /** The button for generating the report based on the email address. */ 159 protected Button m_searchByEmail; 160 161 /** Manages the dynamic text filter fields. */ 162 protected CmsEditableGroup m_filterGroup; 163 164 /** The widget containing the report/results. */ 165 protected VerticalLayout m_resultsContainer; 166 167 /** The label for the results. */ 168 protected Label m_resultsLabel; 169 170 /** The button used to download the report. */ 171 protected Button m_download; 172 173 /** The button used to select an OpenCms user for whom to generate the report. */ 174 protected Button m_pickUserButton; 175 176 /** The field containing the name of the user for whom to generate the report. */ 177 protected TextField m_user; 178 179 /** The button which generates the report for the selected OpenCms user. */ 180 protected Button m_searchByUser; 181 182 /** The report as a string (the content of the download). */ 183 protected String m_result; 184 185 /** The m report overlay. */ 186 protected AtomicReference<CmsReportOverlay> m_reportOverlay = new AtomicReference<>(); 187 188 /** 189 * Creates a new instance. 190 */ 191 public CmsUserDataAppPanel() { 192 193 CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), new HashMap<>()); 194 m_resultsLabel.setContentMode(ContentMode.HTML); 195 m_resultsLabel.setWidth("100%"); 196 m_resultsLabel.addStyleName(O_USERDATA_CONTAINER); 197 m_resultsContainer.setVisible(false); 198 m_filterGroup = new CmsEditableGroup(m_filters, () -> { 199 TextField filterField = new TextField(); 200 return filterField; 201 }, CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_USERDATA_TEXT_FILTER_ADD_0)); 202 m_filterGroup.setRowCaption( 203 CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_USERDATA_TEXT_FILTER_0)); 204 m_filterGroup.addRow(new TextField()); 205 FileDownloader downloader = new FileDownloader(new StreamResource(() -> { 206 String result = CmsUserDataAppPanel.this.getResult(); 207 byte[] resultBytes = result.getBytes(StandardCharsets.UTF_8); 208 return new ByteArrayInputStream(resultBytes); 209 }, "userdata.html")); 210 downloader.extend(m_download); 211 m_download.setIcon(VaadinIcons.DOWNLOAD); 212 m_pickUserButton.setCaption(""); 213 m_pickUserButton.setIcon(FontAwesome.USER); 214 m_pickUserButton.addStyleName(OpenCmsTheme.BUTTON_ICON); 215 m_pickUserButton.addClickListener(evt -> { 216 final Window window = CmsBasicDialog.prepareWindow(DialogWidth.max); 217 218 CmsPrincipalSelectDialog dialog = new CmsPrincipalSelectDialog( 219 principal -> selectUser(principal), 220 "", 221 window, 222 WidgetType.userwidget, 223 true, 224 CmsPrincipalSelect.PrincipalType.user); 225 dialog.setOuComboBoxEnabled(true); 226 window.setCaption(CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_USERDATA_SELECT_USER_0)); 227 window.setContent(dialog); 228 A_CmsUI.get().addWindow(window); 229 }); 230 231 m_searchByEmail.addClickListener(event -> { 232 233 hideResult(); 234 List<String> filterStrings = new ArrayList<>(); 235 m_filterGroup.getRows().stream().forEach(row -> { 236 if (row.getComponent() instanceof TextField) { 237 TextField textField = (TextField)row.getComponent(); 238 String value = textField.getValue(); 239 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(value)) { 240 value = value.trim(); 241 filterStrings.add(value); 242 } 243 } 244 }); 245 246 String email = m_email.getValue().trim(); 247 CmsObject cms = getCmsObjectForReport(); 248 try { 249 Document doc = Jsoup.parseBodyFragment(""); 250 doc.body().addClass(O_USERDATA_CONTAINER); 251 doc.head().append("<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />"); 252 try (InputStream stream = getClass().getClassLoader().getResourceAsStream( 253 "VAADIN/themes/opencms/userdata.css")) { 254 String style = new String(CmsFileUtil.readFully(stream, false), StandardCharsets.UTF_8); 255 doc.head().append("<style>\n" + style + "\n</style>\n"); 256 } 257 @SuppressWarnings("synthetic-access") 258 Status status = new Status(); 259 List<String> headerSearchTerms = new ArrayList<>(); 260 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(email)) { 261 headerSearchTerms.add(email); 262 } 263 headerSearchTerms.addAll(filterStrings); 264 String headerSearchTermsString = headerSearchTerms.stream().map(s -> "\"" + s + "\"").collect( 265 Collectors.joining(", ")); 266 doc.body().appendElement("h1").attr("class", "udr-header").text( 267 Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 268 Messages.GUI_USER_INFORMATION_FOR_1, 269 headerSearchTermsString)); 270 271 CmsUserDataReportThread thread = new CmsUserDataReportThread(cms, report -> { 272 try { 273 boolean changed = OpenCms.getUserDataRequestManager().getInfoForEmail( 274 cms, 275 I_CmsUserDataDomain.Mode.workplace, 276 email, 277 filterStrings, 278 doc.body(), 279 report); 280 status.setChanged(changed); 281 } catch (CmsException e) { 282 LOG.error(e.getLocalizedMessage(), e); 283 status.setException(e); 284 } 285 }); 286 if (m_reportOverlay.get() != null) { 287 removeComponent(m_reportOverlay.get()); 288 m_reportOverlay.set(null); 289 } 290 m_reportOverlay.set(new CmsReportOverlay(thread)); 291 addComponent(m_reportOverlay.get()); 292 m_reportOverlay.get().addReportFinishedHandler(() -> { 293 if (status.getException() != null) { 294 CmsErrorDialog.showErrorDialog(status.getException()); 295 } else if (status.isChanged()) { 296 showResult(doc); 297 } else { 298 showResult(null); 299 } 300 }); 301 thread.start(); 302 } catch (Exception e) { 303 CmsErrorDialog.showErrorDialog(e); 304 } 305 }); 306 307 m_searchByUser.addClickListener(evt -> { 308 hideResult(); 309 m_user.setComponentError(null); 310 String user = m_user.getValue().trim(); 311 CmsObject cms = getCmsObjectForReport(); 312 CmsUser userObj = null; 313 try { 314 userObj = cms.readUser(user); 315 } catch (Exception e) { 316 LOG.info(e.getLocalizedMessage(), e); 317 m_user.setComponentError( 318 new UserError( 319 CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_USERDATA_USER_NOT_FOUND_0))); 320 return; 321 } 322 final CmsUser finalUser = userObj; 323 try { 324 Document doc = Jsoup.parseBodyFragment(""); 325 @SuppressWarnings("synthetic-access") 326 Status status = new Status(); 327 doc.body().appendElement("h1").attr("class", "udr-header").text( 328 Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 329 Messages.GUI_USER_INFORMATION_FOR_1, 330 user)); 331 332 CmsUserDataReportThread thread = new CmsUserDataReportThread(cms, report -> { 333 try { 334 boolean changed = OpenCms.getUserDataRequestManager().getInfoForUser( 335 cms, 336 I_CmsUserDataDomain.Mode.workplace, 337 finalUser, 338 doc.body(), 339 report); 340 status.setChanged(changed); 341 } catch (CmsException e) { 342 LOG.error(e.getLocalizedMessage(), e); 343 status.setException(e); 344 } 345 }); 346 if (m_reportOverlay.get() != null) { 347 removeComponent(m_reportOverlay.get()); 348 m_reportOverlay.set(null); 349 } 350 m_reportOverlay.set(new CmsReportOverlay(thread)); 351 addComponent(m_reportOverlay.get()); 352 m_reportOverlay.get().addReportFinishedHandler(() -> { 353 if (status.getException() != null) { 354 CmsErrorDialog.showErrorDialog(status.getException()); 355 } else if (status.isChanged()) { 356 showResult(doc); 357 } else { 358 showResult(null); 359 } 360 }); 361 thread.start(); 362 } catch (Exception e) { 363 CmsErrorDialog.showErrorDialog(e); 364 } 365 }); 366 } 367 368 /** 369 * Prepares the CmsObject to use for the report. 370 * 371 * <p>The CmsObject's locale needs to be set to the current locale, because the plugins 372 * used to generate the report do not use a separate locale parameter. 373 * 374 * @return the CmsObject to use for generating the report 375 */ 376 protected CmsObject getCmsObjectForReport() { 377 378 CmsObject cms = A_CmsUI.getCmsObject(); 379 try { 380 cms = OpenCms.initCmsObject(cms); 381 cms.getRequestContext().setLocale(OpenCms.getWorkplaceManager().getWorkplaceLocale(cms)); 382 } catch (Exception e) { 383 LOG.error(e.getLocalizedMessage(), e); 384 } 385 return cms; 386 } 387 388 /** 389 * Gets the report for the download. 390 * 391 * @return the report as HTML text 392 */ 393 protected String getResult() { 394 395 return m_result; 396 } 397 398 /** 399 * Hides the previous search results. 400 */ 401 private void hideResult() { 402 403 m_resultsLabel.setValue(""); 404 m_resultsContainer.setVisible(false); 405 } 406 407 /** 408 * Called when an OpenCms user is selected. 409 * 410 * @param principal the selected user 411 */ 412 private void selectUser(I_CmsPrincipal principal) { 413 414 CmsUser user = (CmsUser)principal; 415 m_user.setComponentError(null); 416 m_user.setValue(user.getName()); 417 } 418 419 /** 420 * Shows the report. 421 * 422 * <p>If null is given an argument, reports previously shown are hidden. 423 * 424 * @param doc the report (or null, to hide previous results) 425 */ 426 private void showResult(Document doc) { 427 428 if (doc != null) { 429 m_resultsContainer.setVisible(true); 430 m_resultsLabel.setValue(doc.body().html()); 431 m_result = "<!DOCTYPE html>\n" + doc.toString(); 432 } else { 433 hideResult(); 434 String notfound = CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_USERDATA_NOT_FOUND_0); 435 Notification notification = new Notification( 436 "", 437 "<p>" + CmsEncoder.escapeXml(notfound) + "</p>", 438 Notification.Type.WARNING_MESSAGE, 439 true); 440 notification.setDelayMsec(-1); 441 notification.show(Page.getCurrent()); 442 443 } 444 } 445 446}