001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (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.gwt.client.seo; 029 030import org.opencms.gwt.client.CmsCoreProvider; 031import org.opencms.gwt.client.Messages; 032import org.opencms.gwt.client.property.A_CmsPropertyEditor; 033import org.opencms.gwt.client.property.CmsPropertySubmitHandler; 034import org.opencms.gwt.client.property.CmsSimplePropertyEditor; 035import org.opencms.gwt.client.property.I_CmsPropertyEditorHandler; 036import org.opencms.gwt.client.rpc.CmsRpcAction; 037import org.opencms.gwt.client.ui.CmsFieldSet; 038import org.opencms.gwt.client.ui.CmsListItemWidget; 039import org.opencms.gwt.client.ui.CmsPopup; 040import org.opencms.gwt.client.ui.CmsPushButton; 041import org.opencms.gwt.client.ui.CmsScrollPanel; 042import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle; 043import org.opencms.gwt.client.ui.input.I_CmsFormField; 044import org.opencms.gwt.client.ui.input.form.CmsForm; 045import org.opencms.gwt.client.ui.input.form.I_CmsFormHandler; 046import org.opencms.gwt.client.ui.input.form.I_CmsFormSubmitHandler; 047import org.opencms.gwt.shared.CmsListInfoBean; 048import org.opencms.gwt.shared.CmsListInfoBean.StateIcon; 049import org.opencms.gwt.shared.alias.CmsAliasBean; 050import org.opencms.util.CmsUUID; 051import org.opencms.xml.content.CmsXmlContentProperty; 052 053import java.util.LinkedHashMap; 054import java.util.List; 055import java.util.Map; 056 057import com.google.gwt.core.client.GWT; 058import com.google.gwt.dom.client.Style; 059import com.google.gwt.dom.client.Style.Overflow; 060import com.google.gwt.dom.client.Style.Unit; 061import com.google.gwt.event.dom.client.ClickEvent; 062import com.google.gwt.event.dom.client.ClickHandler; 063import com.google.gwt.user.client.Timer; 064import com.google.gwt.user.client.rpc.AsyncCallback; 065import com.google.gwt.user.client.ui.FlowPanel; 066 067/** 068 * The SEO options dialog, which makes it possible to both edit the SEO relevant properties of 069 * a resource as well as alias paths for the resource.<p> 070 */ 071public class CmsSeoOptionsDialog extends CmsPopup implements I_CmsFormHandler { 072 073 /** The alias messages. */ 074 protected static CmsAliasMessages aliasMessages = new CmsAliasMessages(); 075 076 /** The properties which should be displayed. */ 077 protected static String[] seoProperties = new String[] {"Title", "Description", "Keywords"}; 078 079 /** The validation has detected an error. */ 080 protected static final int VALIDATION_FAILED = 2; 081 082 /** The validation has finished successfully. */ 083 protected static final int VALIDATION_OK = 0; 084 085 /** The validation isn't finished yet. */ 086 protected static final int VALIDATION_RUNNING = 1; 087 088 /** The inner alias list. */ 089 protected CmsAliasList m_aliasList; 090 091 /** The validation status for the aliases. */ 092 protected int m_aliasValidationStatus; 093 094 /** The root panel for this dialog. */ 095 protected FlowPanel m_panel = new FlowPanel(); 096 097 /** The validation status for the properties. */ 098 protected int m_propertyValidationStatus; 099 100 /** The structure id of the resource whose aliases are being edited. */ 101 protected CmsUUID m_structureId; 102 103 /** The property editor instance. */ 104 A_CmsPropertyEditor m_propertyEditor; 105 106 /** The form submit handler. */ 107 private I_CmsFormSubmitHandler m_formSubmitHandler; 108 109 /** 110 * The field set containing the properties relevant for SEO. 111 */ 112 private CmsFieldSet m_propertyFieldset = new CmsFieldSet(); 113 114 /** 115 * Creates a new dialog instance.<p> 116 * 117 * @param structureId the structure id of the resource whose aliases are being edited 118 * @param infoBean a bean containing the information to display in the resource info box 119 * @param aliases the existing aliases of the resource 120 * @param propertyConfig the property configuration 121 * @param propertyEditorHandler the property editor handler 122 */ 123 public CmsSeoOptionsDialog( 124 CmsUUID structureId, 125 CmsListInfoBean infoBean, 126 List<CmsAliasBean> aliases, 127 Map<String, CmsXmlContentProperty> propertyConfig, 128 I_CmsPropertyEditorHandler propertyEditorHandler) { 129 130 super(aliasMessages.seoOptions()); 131 setGlassEnabled(true); 132 setAutoHideEnabled(false); 133 setModal(true); 134 135 //-----------------------INFO BOX ------------------------------------------- 136 137 CmsListItemWidget liWidget = new CmsListItemWidget(infoBean); 138 liWidget.setStateIcon(StateIcon.standard); 139 m_panel.add(liWidget); 140 141 //------------------------ PROPERTIES ------------------------------------------ 142 LinkedHashMap<String, CmsXmlContentProperty> props = new LinkedHashMap<String, CmsXmlContentProperty>(); 143 for (String seoProperty : seoProperties) { 144 if (propertyConfig.containsKey(seoProperty)) { 145 props.put(seoProperty, propertyConfig.get(seoProperty)); 146 } 147 } 148 m_propertyEditor = new CmsSimplePropertyEditor(props, propertyEditorHandler); 149 m_propertyEditor.getForm().setFormHandler(this); 150 m_formSubmitHandler = new CmsPropertySubmitHandler(propertyEditorHandler); 151 m_structureId = structureId; 152 m_propertyFieldset.getElement().getStyle().setMarginTop(10, Unit.PX); 153 m_propertyFieldset.getContentPanel().getElement().getStyle().setOverflow(Overflow.VISIBLE); 154 m_propertyFieldset.setLegend( 155 org.opencms.gwt.client.Messages.get().key(org.opencms.gwt.client.Messages.GUI_PROPERTIES_0)); 156 m_propertyEditor.initializeWidgets(this); 157 m_panel.add(m_propertyFieldset); 158 159 //------------------------ ALIASES ------------------------------------------ 160 161 CmsFieldSet aliasFieldset = new CmsFieldSet(); 162 aliasFieldset.setLegend(aliasMessages.aliases()); 163 m_aliasList = new CmsAliasList(structureId, aliases); 164 aliasFieldset.getElement().getStyle().setMarginTop(10, Unit.PX); 165 CmsScrollPanel scrollPanel = GWT.create(CmsScrollPanel.class); 166 scrollPanel.setWidget(m_aliasList); 167 aliasFieldset.addContent(scrollPanel); 168 m_panel.add(scrollPanel); 169 Style style = scrollPanel.getElement().getStyle(); 170 style.setProperty("minHeight", "300px"); //$NON-NLS-1$ //$NON-NLS-2$ 171 style.setProperty("maxHeight", "450px"); //$NON-NLS-1$ //$NON-NLS-2$ 172 style.setOverflowY(Overflow.AUTO); 173 174 setMainContent(m_panel); 175 addButton(createCancelButton()); 176 addButton(saveButton()); 177 178 } 179 180 /** 181 * Loads the aliases for a given page.<p> 182 * 183 * @param structureId the structure id of the page 184 * @param callback the callback for the loaded aliases 185 */ 186 public static void loadAliases(final CmsUUID structureId, final AsyncCallback<List<CmsAliasBean>> callback) { 187 188 final CmsRpcAction<List<CmsAliasBean>> action = new CmsRpcAction<List<CmsAliasBean>>() { 189 190 @Override 191 public void execute() { 192 193 start(200, true); 194 CmsCoreProvider.getVfsService().getAliasesForPage(structureId, this); 195 } 196 197 @Override 198 protected void onResponse(List<CmsAliasBean> result) { 199 200 stop(false); 201 callback.onSuccess(result); 202 } 203 }; 204 action.execute(); 205 } 206 207 public boolean isSubmitting() { 208 209 // TODO Auto-generated method stub 210 return false; 211 } 212 213 /** 214 * @see org.opencms.gwt.client.ui.input.form.I_CmsFormHandler#onSubmitValidationResult(org.opencms.gwt.client.ui.input.form.CmsForm, boolean) 215 */ 216 public void onSubmitValidationResult(CmsForm form, boolean ok) { 217 218 m_propertyValidationStatus = ok ? VALIDATION_OK : VALIDATION_FAILED; 219 update(true); 220 } 221 222 /** 223 * @see org.opencms.gwt.client.ui.input.form.I_CmsFormHandler#onValidationResult(org.opencms.gwt.client.ui.input.form.CmsForm, boolean) 224 */ 225 public void onValidationResult(CmsForm form, boolean ok) { 226 227 m_propertyValidationStatus = ok ? VALIDATION_OK : VALIDATION_FAILED; 228 update(false); 229 } 230 231 /** 232 * Saves the aliases for a given page.<p> 233 * 234 * @param uuid the page structure id 235 * @param aliases the aliases to save 236 */ 237 public void saveAliases(final CmsUUID uuid, final List<CmsAliasBean> aliases) { 238 239 final CmsRpcAction<Void> action = new CmsRpcAction<Void>() { 240 241 /** 242 * @see org.opencms.gwt.client.rpc.CmsRpcAction#execute() 243 */ 244 @Override 245 public void execute() { 246 247 start(200, true); 248 CmsCoreProvider.getVfsService().saveAliases(uuid, aliases, this); 249 250 } 251 252 /** 253 * @see org.opencms.gwt.client.rpc.CmsRpcAction#onResponse(java.lang.Object) 254 */ 255 @Override 256 public void onResponse(Void result) { 257 258 stop(false); 259 } 260 261 }; 262 action.execute(); 263 } 264 265 /** 266 * @see org.opencms.gwt.client.ui.CmsPopup#show() 267 */ 268 @Override 269 public void show() { 270 271 m_propertyFieldset.addContent(m_propertyEditor.getForm().getWidget()); 272 m_propertyFieldset.addStyleName(I_CmsInputLayoutBundle.INSTANCE.inputCss().formGradientBackground()); 273 super.show(); 274 notifyWidgetsOfOpen(); 275 } 276 277 /** 278 * Updates the validation status and optionally submits the data.<p> 279 * 280 * @param submit the submit flag 281 */ 282 public void update(boolean submit) { 283 284 boolean ok = (m_propertyValidationStatus == VALIDATION_OK) && (m_aliasValidationStatus == VALIDATION_OK); 285 if (submit && ok) { 286 saveProperties(); 287 saveAliases(); 288 hide(); 289 } 290 } 291 292 /** 293 * The method which is called when the user clicks the save button of the dialog.<p> 294 */ 295 protected void onClickSave() { 296 297 Timer timer = new Timer() { 298 299 @Override 300 public void run() { 301 302 m_aliasList.clearValidationErrors(); 303 m_aliasValidationStatus = VALIDATION_RUNNING; 304 m_propertyValidationStatus = VALIDATION_RUNNING; 305 m_aliasList.validate(new Runnable() { 306 307 public void run() { 308 309 m_aliasValidationStatus = !m_aliasList.hasValidationErrors() 310 ? VALIDATION_OK 311 : VALIDATION_FAILED; 312 update(true); 313 314 } 315 }); 316 m_propertyEditor.getForm().validateAndSubmit(); 317 } 318 }; 319 // slight delay so that the validation doesn't interfere with validations triggered by the change event 320 timer.schedule(20); 321 } 322 323 /** 324 * Saves the aliases.<p> 325 */ 326 protected void saveAliases() { 327 328 List<CmsAliasBean> aliases = m_aliasList.getAliases(); 329 saveAliases(m_structureId, aliases); 330 } 331 332 /** 333 * Saves the properties.<p> 334 */ 335 protected void saveProperties() { 336 337 m_propertyEditor.getForm().handleSubmit(m_formSubmitHandler); 338 } 339 340 /** 341 * Creates the cancel button.<p> 342 * 343 * @return the cancel button 344 */ 345 private CmsPushButton createCancelButton() { 346 347 addDialogClose(null); 348 CmsPushButton button = new CmsPushButton(); 349 button.setText(Messages.get().key(Messages.GUI_CANCEL_0)); 350 button.setUseMinWidth(true); 351 button.addClickHandler(new ClickHandler() { 352 353 /** 354 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 355 */ 356 public void onClick(ClickEvent event) { 357 358 CmsSeoOptionsDialog.this.hide(); 359 } 360 }); 361 362 return button; 363 } 364 365 /** 366 * Tells all widgets that the dialog has been opened.<p> 367 */ 368 private void notifyWidgetsOfOpen() { 369 370 for (Map.Entry<String, I_CmsFormField> fieldEntry : m_propertyEditor.getForm().getFields().entrySet()) { 371 fieldEntry.getValue().getWidget().setAutoHideParent(this); 372 } 373 } 374 375 /** 376 * Creates the OK button.<p> 377 * 378 * @return the OK button 379 */ 380 private CmsPushButton saveButton() { 381 382 CmsPushButton button = new CmsPushButton(); 383 button.setText(Messages.get().key(Messages.GUI_SAVE_0)); 384 button.setUseMinWidth(true); 385 button.addClickHandler(new ClickHandler() { 386 387 /** 388 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 389 */ 390 public void onClick(ClickEvent event) { 391 392 CmsSeoOptionsDialog.this.onClickSave(); 393 } 394 }); 395 return button; 396 } 397 398}