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.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.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.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 m_fieldName.setComponentError(null); 272 CmsObject cms = A_CmsUI.getCmsObject(); 273 String projectName = CmsStringUtil.joinPaths(m_fieldOU.getValue(), m_fieldName.getValue()); 274 try { 275 cms.readProject(projectName); 276 throw new InvalidValueException(CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_PROJECT_WITH_SAME_NAME_EXISTS_0)); 277 278 } catch (CmsException e) { 279 // ignore 280 } 281 } 282 }); 283 } 284 m_fieldOU.addValueChangeListener(new ValueChangeListener() { 285 286 private static final long serialVersionUID = 1L; 287 288 public void valueChange(ValueChangeEvent event) { 289 290 if (m_project == null) { 291 // the 'project exists' validation is attached to m_fieldName, but is also dependent on the value of the OU field, 292 // so we trigger it here 293 try { 294 m_fieldName.setComponentError(null); 295 m_fieldName.validate(); 296 } catch (InvalidValueException e) { 297 m_fieldName.setComponentError(new UserError(e.getMessage())); 298 } 299 } 300 validateResourceFields(); 301 } 302 }); 303 304 m_addResource.addClickListener(new ClickListener() { 305 306 private static final long serialVersionUID = 1L; 307 308 public void buttonClick(ClickEvent event) { 309 310 addResourceField(null); 311 } 312 }); 313 m_ok.addClickListener(new ClickListener() { 314 315 private static final long serialVersionUID = 1L; 316 317 public void buttonClick(ClickEvent event) { 318 319 submit(); 320 } 321 }); 322 m_cancel.addClickListener(new ClickListener() { 323 324 private static final long serialVersionUID = 1L; 325 326 public void buttonClick(ClickEvent event) { 327 328 cancel(); 329 } 330 }); 331 m_fields = new Field<?>[] {m_fieldName, m_fieldDescription, m_fieldManager, m_fieldUser, m_fieldOU}; 332 } 333 334 /** 335 * Adds a new resource field.<p> 336 * 337 * @param value the value to set 338 */ 339 void addResourceField(String value) { 340 341 CmsPathSelectField field = new CmsPathSelectField(); 342 field.setUseRootPaths(true); 343 if (value != null) { 344 field.setValue(value); 345 } 346 field.addValidator(m_resourceValidator); 347 CmsRemovableFormRow<CmsPathSelectField> row = new CmsRemovableFormRow<CmsPathSelectField>( 348 field, 349 CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_REMOVE_RESOURCE_0)); 350 row.setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_RESOURCE_0)); 351 row.setDescription(CmsVaadinUtils.getMessageText(Messages.GUI_PROJECTS_RESOURCE_HELP_0)); 352 m_resources.addComponent(row); 353 } 354 355 /** 356 * Cancels project edit.<p> 357 */ 358 void cancel() { 359 360 m_window.close(); 361 } 362 363 /** 364 * Submits the form.<p> 365 */ 366 void submit() { 367 368 if (isValid()) { 369 if (m_project == null) { 370 createProject(); 371 } else { 372 saveProject(); 373 } 374 m_table.loadProjects(); 375 m_window.close(); 376 } 377 } 378 379 /** 380 * Validates the resource fields.<p> 381 */ 382 @SuppressWarnings("unchecked") 383 void validateResourceFields() { 384 385 for (Component c : m_resources) { 386 if (c instanceof CmsRemovableFormRow<?>) { 387 ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().validate(); 388 } 389 } 390 } 391 392 /** 393 * Creates a new project.<p> 394 */ 395 private void createProject() { 396 397 CmsObject cms = A_CmsUI.getCmsObject(); 398 try { 399 String name = "/"; 400 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldOU.getValue())) { 401 name = CmsStringUtil.joinPaths(name, m_fieldOU.getValue()); 402 } 403 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_fieldName.getValue())) { 404 name = CmsStringUtil.joinPaths(name, m_fieldName.getValue()); 405 } else { 406 name = CmsStringUtil.joinPaths(name, "/"); 407 } 408 409 m_project = cms.createProject( 410 name, 411 m_fieldDescription.getValue(), 412 m_fieldUser.getValue(), 413 m_fieldManager.getValue(), 414 m_fieldDeleteAfterPublish.getValue().booleanValue() 415 ? CmsProject.PROJECT_TYPE_TEMPORARY 416 : CmsProject.PROJECT_TYPE_NORMAL); 417 updateProjectResources(); 418 419 } catch (Throwable t) { 420 CmsErrorDialog.showErrorDialog(t); 421 } 422 } 423 424 /** 425 * Returns the selected resource paths.<p> 426 * 427 * @return the resource paths 428 */ 429 @SuppressWarnings("unchecked") 430 private Set<String> getResourcePaths() { 431 432 Set<String> resources = new HashSet<String>(); 433 for (Component c : m_resources) { 434 if (c instanceof CmsRemovableFormRow<?>) { 435 String value = ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().getValue(); 436 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(value)) { 437 resources.add(value); 438 } 439 } 440 } 441 return resources; 442 } 443 444 /** 445 * Validates the form fields.<p> 446 * 447 * @return <code>true</code> in case all fields are valid 448 */ 449 @SuppressWarnings("unchecked") 450 private boolean isValid() { 451 452 for (Field<?> field : m_fields) { 453 if (!field.isValid()) { 454 field.focus(); 455 456 return false; 457 } 458 } 459 for (Component c : m_resources) { 460 if (c instanceof CmsRemovableFormRow<?>) { 461 if (!((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().isValid()) { 462 ((CmsRemovableFormRow<CmsPathSelectField>)c).getInput().focus(); 463 return false; 464 } 465 } 466 } 467 return true; 468 } 469 470 /** 471 * Saves an existing project.<p> 472 */ 473 private void saveProject() { 474 475 CmsObject cms = A_CmsUI.getCmsObject(); 476 try { 477 m_project.setDescription(m_fieldDescription.getValue()); 478 m_project.setGroupId(cms.readGroup(m_fieldUser.getValue()).getId()); 479 m_project.setManagerGroupId(cms.readGroup(m_fieldManager.getValue()).getId()); 480 m_project.setDeleteAfterPublishing(m_fieldDeleteAfterPublish.getValue().booleanValue()); 481 cms.writeProject(m_project); 482 updateProjectResources(); 483 } catch (Throwable t) { 484 CmsErrorDialog.showErrorDialog(t); 485 } 486 } 487 488 /** 489 * Updates the project resources.<p> 490 * 491 * @throws CmsException in case writing the project fails 492 */ 493 private void updateProjectResources() throws CmsException { 494 495 CmsObject cms = A_CmsUI.getCmsObject(); 496 Set<String> resourceRootPaths = getResourcePaths(); 497 // write the edited project resources 498 CmsProject currentProject = cms.getRequestContext().getCurrentProject(); 499 // change the current project 500 cms.getRequestContext().setCurrentProject(m_project); 501 // store the current site root 502 String currentSite = cms.getRequestContext().getSiteRoot(); 503 // copy the resources to the current project 504 try { 505 // switch to the root site 506 cms.getRequestContext().setSiteRoot(""); 507 // remove deleted resources 508 for (String resName : cms.readProjectResources(m_project)) { 509 if (!resourceRootPaths.contains(resName)) { 510 cms.removeResourceFromProject(resName); 511 } 512 } 513 // read project resources again! 514 List<String> currentResNames = cms.readProjectResources(m_project); 515 // copy missing resources 516 for (String resName : resourceRootPaths) { 517 if (!currentResNames.contains(resName)) { 518 cms.copyResourceToProject(resName); 519 } 520 } 521 } finally { 522 // switch back to current site and project 523 cms.getRequestContext().setSiteRoot(currentSite); 524 cms.getRequestContext().setCurrentProject(currentProject); 525 } 526 } 527}