001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.projects; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsProject; 032import org.opencms.file.CmsResource; 033import org.opencms.main.CmsException; 034import org.opencms.main.CmsLog; 035import org.opencms.main.OpenCms; 036import org.opencms.security.CmsOrganizationalUnit; 037import org.opencms.security.CmsRole; 038import org.opencms.ui.A_CmsUI; 039import org.opencms.ui.CmsCssIcon; 040import org.opencms.ui.CmsVaadinUtils; 041import org.opencms.ui.apps.Messages; 042import org.opencms.ui.components.CmsBasicDialog; 043import org.opencms.ui.components.CmsErrorDialog; 044import org.opencms.ui.components.CmsRemovableFormRow; 045import org.opencms.ui.components.CmsResourceInfo; 046import org.opencms.ui.components.OpenCmsTheme; 047import org.opencms.ui.components.fileselect.CmsPathSelectField; 048import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect; 049import org.opencms.ui.dialogs.permissions.CmsPrincipalSelect.WidgetType; 050import org.opencms.util.CmsStringUtil; 051import org.opencms.util.CmsUUID; 052 053import java.util.Collections; 054import java.util.HashSet; 055import java.util.List; 056import java.util.Set; 057 058import org.apache.commons.logging.Log; 059 060import com.vaadin.server.UserError; 061import com.vaadin.ui.Button; 062import com.vaadin.ui.Button.ClickEvent; 063import com.vaadin.ui.Button.ClickListener; 064import com.vaadin.ui.Component; 065import com.vaadin.ui.FormLayout; 066import com.vaadin.ui.UI; 067import com.vaadin.ui.Window; 068import com.vaadin.v7.data.Property.ValueChangeEvent; 069import com.vaadin.v7.data.Property.ValueChangeListener; 070import com.vaadin.v7.data.Validator; 071import com.vaadin.v7.data.Validator.InvalidValueException; 072import com.vaadin.v7.ui.CheckBox; 073import com.vaadin.v7.ui.Field; 074import com.vaadin.v7.ui.TextField; 075 076/** 077 * The edit project form component.<p> 078 */ 079public class CmsEditProjectForm extends CmsBasicDialog { 080 081 /** 082 * The OU validator.<p> 083 */ 084 protected class OUValidator implements Validator { 085 086 /** The serial version id. */ 087 private static final long serialVersionUID = 1L; 088 089 /** 090 * @see com.vaadin.v7.data.Validator#validate(java.lang.Object) 091 */ 092 public void validate(Object value) throws InvalidValueException { 093 094 if (m_fieldOU.isEnabled() && CmsStringUtil.isNotEmptyOrWhitespaceOnly((String)value)) { 095 try { 096 OpenCms.getOrgUnitManager().readOrganizationalUnit(A_CmsUI.getCmsObject(), (String)value); 097 } catch (CmsException e) { 098 throw new InvalidValueException(e.getLocalizedMessage(UI.getCurrent().getLocale())); 099 } 100 } 101 } 102 } 103 104 /** 105 * The resource field validator. Checks whether the resource is part of the project OU.<p> 106 */ 107 protected class ResourceValidator implements Validator { 108 109 /** The serial version id. */ 110 private static final long serialVersionUID = 1L; 111 112 /** 113 * @see com.vaadin.v7.data.Validator#validate(java.lang.Object) 114 */ 115 public void validate(Object value) throws InvalidValueException { 116 117 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly((String)value) 118 && CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldOU.getValue())) { 119 try { 120 List<CmsResource> ouRes = OpenCms.getOrgUnitManager().getResourcesForOrganizationalUnit( 121 A_CmsUI.getCmsObject(), 122 m_fieldOU.getValue()); 123 124 String resPath = (String)value; 125 for (CmsResource res : ouRes) { 126 if (resPath.startsWith(res.getRootPath())) { 127 return; 128 } 129 } 130 throw new InvalidValueException( 131 "The resource path " 132 + value 133 + " is not part of the project OU '" 134 + m_fieldOU.getValue() 135 + "'."); 136 } catch (CmsException e) { 137 // ignore 138 } 139 } 140 } 141 } 142 143 /** The logger for this class. */ 144 private static Log LOG = CmsLog.getLog(CmsEditProjectForm.class.getName()); 145 146 /** The serial version id. */ 147 private static final long serialVersionUID = 2345799706922671537L; 148 149 /** The OU field. */ 150 TextField m_fieldOU; 151 152 /** The add resources button. */ 153 private Button m_addResource; 154 155 /** The cancel button. */ 156 private Button m_cancel; 157 158 /** The delete after publish check box. */ 159 private CheckBox m_fieldDeleteAfterPublish; 160 161 /** The project description field. */ 162 private TextField m_fieldDescription; 163 164 /** The manager group field. */ 165 private CmsPrincipalSelect m_fieldManager; 166 167 /** The project name field. */ 168 private TextField m_fieldName; 169 170 /** The form fileds. */ 171 private Field<?>[] m_fields; 172 173 /** The user group field. */ 174 private CmsPrincipalSelect m_fieldUser; 175 176 /** The projects table instance. */ 177 private CmsProjectsTable m_table; 178 179 /** The OK button. */ 180 private Button m_ok; 181 182 /** The edited project. */ 183 private CmsProject m_project; 184 185 /** The resources form layout. */ 186 private FormLayout m_resources; 187 188 /** The resource field validator. */ 189 private ResourceValidator m_resourceValidator; 190 191 /** The window this form is displayed in. */ 192 private Window m_window; 193 194 /** 195 * Constructor.<p> 196 * Used to edit existing projects.<p> 197 * 198 * @param table the projects table 199 * @param projectId the project to edit 200 * @param window the window this form is displayed in 201 */ 202 public CmsEditProjectForm(CmsProjectsTable table, CmsUUID projectId, Window window) { 203 204 this(table, window); 205 CmsObject cms = A_CmsUI.getCmsObject(); 206 try { 207 m_project = cms.readProject(projectId); 208 displayResourceInfoDirectly( 209 Collections.singletonList( 210 new CmsResourceInfo( 211 m_project.getName(), 212 m_project.getDescription(), 213 new CmsCssIcon(OpenCmsTheme.ICON_PROJECT)))); 214 m_fieldName.setValue(m_project.getName()); 215 m_fieldName.setEnabled(false); 216 m_fieldDescription.setValue(m_project.getDescription()); 217 m_fieldUser.setValue(cms.readGroup(m_project.getGroupId()).getName()); 218 m_fieldManager.setValue(cms.readGroup(m_project.getManagerGroupId()).getName()); 219 try { 220 CmsOrganizationalUnit ou; 221 ou = OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, m_project.getOuFqn()); 222 m_fieldOU.setValue(ou.getDisplayName(UI.getCurrent().getLocale())); 223 } catch (CmsException e) { 224 LOG.error(e.getLocalizedMessage(), e); 225 m_fieldOU.setValue(null); 226 } 227 m_fieldOU.setEnabled(false); 228 for (String resName : cms.readProjectResources(m_project)) { 229 addResourceField(resName); 230 } 231 } catch (CmsException e) { 232 CmsErrorDialog.showErrorDialog(e); 233 } 234 235 } 236 237 /** 238 * Constructor.<p> 239 * Use this to create a new project.<p> 240 * 241 * @param table the projects table 242 * @param window the window this form is displayed in 243 */ 244 public CmsEditProjectForm(CmsProjectsTable table, Window window) { 245 246 m_window = window; 247 m_table = table; 248 CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null); 249 m_resourceValidator = new ResourceValidator(); 250 m_fieldManager.setWidgetType(WidgetType.groupwidget); 251 m_fieldManager.setRealPrincipalsOnly(true); 252 m_fieldUser.setWidgetType(WidgetType.groupwidget); 253 m_fieldUser.setRealPrincipalsOnly(true); 254 255 try { 256 CmsOrganizationalUnit ou = OpenCms.getRoleManager().getOrgUnitsForRole( 257 A_CmsUI.getCmsObject(), 258 CmsRole.PROJECT_MANAGER, 259 true).get(0); 260 m_fieldOU.setValue(ou.getName()); 261 } catch (CmsException e) { 262 LOG.error(e.getLocalizedMessage(), e); 263 m_fieldOU.setValue(null); 264 } 265 m_fieldOU.setImmediate(true); 266 m_fieldOU.addValidator(new OUValidator()); 267 if (m_project == null) { 268 m_fieldName.addValidator(new Validator() { 269 270 public void validate(Object value) throws InvalidValueException { 271 272 m_fieldName.setComponentError(null); 273 CmsObject cms = A_CmsUI.getCmsObject(); 274 String projectName = CmsStringUtil.joinPaths(m_fieldOU.getValue(), m_fieldName.getValue()); 275 try { 276 cms.readProject(projectName); 277 throw new InvalidValueException( 278 CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_PROJECT_WITH_SAME_NAME_EXISTS_0)); 279 280 } catch (CmsException e) { 281 // ignore 282 } 283 } 284 }); 285 } 286 m_fieldOU.addValueChangeListener(new ValueChangeListener() { 287 288 private static final long serialVersionUID = 1L; 289 290 public void valueChange(ValueChangeEvent event) { 291 292 if (m_project == null) { 293 // the 'project exists' validation is attached to m_fieldName, but is also dependent on the value of the OU field, 294 // so we trigger it here 295 try { 296 m_fieldName.setComponentError(null); 297 m_fieldName.validate(); 298 } catch (InvalidValueException e) { 299 m_fieldName.setComponentError(new UserError(e.getMessage())); 300 } 301 } 302 validateResourceFields(); 303 } 304 }); 305 306 m_addResource.addClickListener(new ClickListener() { 307 308 private static final long serialVersionUID = 1L; 309 310 public void buttonClick(ClickEvent event) { 311 312 addResourceField(null); 313 } 314 }); 315 m_ok.addClickListener(new ClickListener() { 316 317 private static final long serialVersionUID = 1L; 318 319 public void buttonClick(ClickEvent event) { 320 321 submit(); 322 } 323 }); 324 m_cancel.addClickListener(new ClickListener() { 325 326 private static final long serialVersionUID = 1L; 327 328 public void buttonClick(ClickEvent event) { 329 330 cancel(); 331 } 332 }); 333 m_fields = new Field<?>[] {m_fieldName, m_fieldDescription, m_fieldManager, m_fieldUser, m_fieldOU}; 334 } 335 336 /** 337 * Adds a new resource field.<p> 338 * 339 * @param value the value to set 340 */ 341 void addResourceField(String value) { 342 343 CmsPathSelectField field = new CmsPathSelectField(); 344 field.setUseRootPaths(true); 345 if (value != null) { 346 field.setValue(value); 347 } 348 field.addValidator(m_resourceValidator); 349 CmsRemovableFormRow<CmsPathSelectField> row = new CmsRemovableFormRow<CmsPathSelectField>( 350 field, 351 CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_REMOVE_RESOURCE_0)); 352 row.setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_RESOURCE_0)); 353 row.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_RESOURCE_HELP_0)); 354 m_resources.addComponent(row); 355 } 356 357 /** 358 * Cancels project edit.<p> 359 */ 360 void cancel() { 361 362 m_window.close(); 363 } 364 365 /** 366 * Submits the form.<p> 367 */ 368 void submit() { 369 370 if (isValid()) { 371 if (m_project == null) { 372 createProject(); 373 } else { 374 saveProject(); 375 } 376 m_table.loadProjects(); 377 m_window.close(); 378 } 379 } 380 381 /** 382 * Validates the resource fields.<p> 383 */ 384 @SuppressWarnings("unchecked") 385 void validateResourceFields() { 386 387 for (Component c : m_resources) { 388 if (c instanceof CmsRemovableFormRow<?>) { 389 ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().validate(); 390 } 391 } 392 } 393 394 /** 395 * Creates a new project.<p> 396 */ 397 private void createProject() { 398 399 CmsObject cms = A_CmsUI.getCmsObject(); 400 try { 401 String name = "/"; 402 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldOU.getValue())) { 403 name = CmsStringUtil.joinPaths(name, m_fieldOU.getValue()); 404 } 405 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldName.getValue())) { 406 name = CmsStringUtil.joinPaths(name, m_fieldName.getValue()); 407 } else { 408 name = CmsStringUtil.joinPaths(name, "/"); 409 } 410 411 m_project = cms.createProject( 412 name, 413 m_fieldDescription.getValue(), 414 m_fieldUser.getValue(), 415 m_fieldManager.getValue(), 416 m_fieldDeleteAfterPublish.getValue().booleanValue() 417 ? CmsProject.PROJECT_TYPE_TEMPORARY 418 : CmsProject.PROJECT_TYPE_NORMAL); 419 updateProjectResources(); 420 421 } catch (Throwable t) { 422 CmsErrorDialog.showErrorDialog(t); 423 } 424 } 425 426 /** 427 * Returns the selected resource paths.<p> 428 * 429 * @return the resource paths 430 */ 431 @SuppressWarnings("unchecked") 432 private Set<String> getResourcePaths() { 433 434 Set<String> resources = new HashSet<String>(); 435 for (Component c : m_resources) { 436 if (c instanceof CmsRemovableFormRow<?>) { 437 String value = ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().getValue(); 438 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(value)) { 439 resources.add(value); 440 } 441 } 442 } 443 return resources; 444 } 445 446 /** 447 * Validates the form fields.<p> 448 * 449 * @return <code>true</code> in case all fields are valid 450 */ 451 @SuppressWarnings("unchecked") 452 private boolean isValid() { 453 454 for (Field<?> field : m_fields) { 455 if (!field.isValid()) { 456 field.focus(); 457 458 return false; 459 } 460 } 461 for (Component c : m_resources) { 462 if (c instanceof CmsRemovableFormRow<?>) { 463 if (!((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().isValid()) { 464 ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().focus(); 465 return false; 466 } 467 } 468 } 469 return true; 470 } 471 472 /** 473 * Saves an existing project.<p> 474 */ 475 private void saveProject() { 476 477 CmsObject cms = A_CmsUI.getCmsObject(); 478 try { 479 m_project.setDescription(m_fieldDescription.getValue()); 480 m_project.setGroupId(cms.readGroup(m_fieldUser.getValue()).getId()); 481 m_project.setManagerGroupId(cms.readGroup(m_fieldManager.getValue()).getId()); 482 m_project.setDeleteAfterPublishing(m_fieldDeleteAfterPublish.getValue().booleanValue()); 483 cms.writeProject(m_project); 484 updateProjectResources(); 485 } catch (Throwable t) { 486 CmsErrorDialog.showErrorDialog(t); 487 } 488 } 489 490 /** 491 * Updates the project resources.<p> 492 * 493 * @throws CmsException in case writing the project fails 494 */ 495 private void updateProjectResources() throws CmsException { 496 497 CmsObject cms = A_CmsUI.getCmsObject(); 498 Set<String> resourceRootPaths = getResourcePaths(); 499 // write the edited project resources 500 CmsProject currentProject = cms.getRequestContext().getCurrentProject(); 501 // change the current project 502 cms.getRequestContext().setCurrentProject(m_project); 503 // store the current site root 504 String currentSite = cms.getRequestContext().getSiteRoot(); 505 // copy the resources to the current project 506 try { 507 // switch to the root site 508 cms.getRequestContext().setSiteRoot(""); 509 // remove deleted resources 510 for (String resName : cms.readProjectResources(m_project)) { 511 if (!resourceRootPaths.contains(resName)) { 512 cms.removeResourceFromProject(resName); 513 } 514 } 515 // read project resources again! 516 List<String> currentResNames = cms.readProjectResources(m_project); 517 // copy missing resources 518 for (String resName : resourceRootPaths) { 519 if (!currentResNames.contains(resName)) { 520 cms.copyResourceToProject(resName); 521 } 522 } 523 } finally { 524 // switch back to current site and project 525 cms.getRequestContext().setSiteRoot(currentSite); 526 cms.getRequestContext().setCurrentProject(currentProject); 527 } 528 } 529}