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