From 38c421efd7a1db6b48cdd36935119f493c8e97d9 Mon Sep 17 00:00:00 2001 From: jensp Date: Tue, 30 Aug 2016 12:16:49 +0000 Subject: [PATCH] CCM NG: - Refactored BasicItemForm - Prepared FolderForm for refactoring git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4233 8810af33-2d31-482b-a856-94f89814c4df --- .../cms/ui/authoring/BasicItemForm.java | 477 ++++++++++++++++++ .../cms/ui/folder/FolderEditor.java.off | 8 +- .../cms/ui/folder/FolderForm.java.off | 138 +++++ 3 files changed, 620 insertions(+), 3 deletions(-) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/BasicItemForm.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderForm.java.off diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/BasicItemForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/BasicItemForm.java new file mode 100755 index 000000000..312561c58 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/BasicItemForm.java @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.authoring; + +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.FormSection; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SaveCancelSection; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormValidationListener; +import com.arsdigita.bebop.form.Hidden; +import com.arsdigita.bebop.form.TextField; +import com.arsdigita.bebop.parameters.NotNullValidationListener; +import com.arsdigita.bebop.parameters.TrimmedStringParameter; +import com.arsdigita.bebop.parameters.URLTokenValidationListener; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.web.Web; +import com.arsdigita.xml.Element; +import org.apache.log4j.Logger; + +import org.librecms.CmsConstants; + +/** + * A form for editing subclasses of ContentItem. This is just a convenience + * class. + * + * @author Jens Pelzetter + * @author Stanislav Freidin + */ +public abstract class BasicItemForm extends FormSection + implements FormInitListener, + FormProcessListener, + FormValidationListener { + + /** + * Internal logger instance to faciliate debugging. Enable logging output by + * editing /WEB-INF/conf/log4j.properties int hte runtime environment and + * set com.arsdigita.cms.ui.BasicItemForm=DEBUG by uncommenting or adding + * the line. + */ + private static final Logger s_log = Logger.getLogger(BasicItemForm.class); + + private final ItemSelectionModel m_itemModel; + private SaveCancelSection m_saveCancelSection; + private final FormSection m_widgetSection; + public static final String CONTENT_ITEM_ID = "ContentItemId"; + public static final String NAME = "ContentItemName"; + public static final String TITLE = "ContentPageTitle"; + public static final String LANGUAGE = "ContentItemLanguage"; + + /** + * Construct a new BasicItemForm with 2 ColumnPanels and add basic content. + * The left Panel is used for Labels, the right Panel for values. + * + * @param formName the name of this form + * @param itemModel The {@link ItemSelectionModel} which will be responsible + * for loading the current item + */ + public BasicItemForm(String formName, ItemSelectionModel itemModel) { + + super(new ColumnPanel(2)); + + m_widgetSection = new FormSection(new ColumnPanel(2, true)); + + super.add(m_widgetSection, ColumnPanel.INSERT); + m_itemModel = itemModel; + + /* Prepare Panel design */ + ColumnPanel panel = (ColumnPanel) getPanel(); + panel.setBorder(false); + panel.setPadColor("#FFFFFF"); + panel.setColumnWidth(1, "20%"); + panel.setColumnWidth(2, "80%"); + panel.setWidth("100%"); + + /* Add basic contents */ + addWidgets(); + + m_saveCancelSection = new SaveCancelSection(); + super.add(m_saveCancelSection, + ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + + addInitListener(this); + addProcessListener(this); + addValidationListener(this); + } + + /** + * Construct a new BasicItemForm with a specified number of ColumnPanels and + * without any content. + * + * @param formName the name of this form + * @param columnPanel the columnpanel of the form + * @param itemModel The {@link ItemSelectionModel} which will be responsible + * for loading the current item + */ + public BasicItemForm(String formName, + ColumnPanel columnPanel, + ItemSelectionModel itemModel) { + super(columnPanel); + + m_widgetSection = new FormSection(new ColumnPanel(columnPanel. + getNumCols(), + true)); + super.add(m_widgetSection, ColumnPanel.INSERT); + m_itemModel = itemModel; + } + + /** + * instanciate and add the save/cancel section for this form + */ + public void addSaveCancelSection() { + m_saveCancelSection = new SaveCancelSection(); + super. + add(m_saveCancelSection, ColumnPanel.FULL_WIDTH + | ColumnPanel.LEFT); + } + + /** + * Currently, to insert javascript code the Label Widget is "abused". + */ + private final Label m_script = new Label(String.format( + "", + Web.getWebappContextPath()), + false); + + /** + * Add basic widgets to the form. + * + * Widgets added are 'title' and 'name (url)' which are part of any content + * item. Child classes will override this method to perform all their + * widget-adding needs but are supposed to use super() to add the basic + * widgets. + */ + protected void addWidgets() { + //add(new FormErrorDisplay(this), ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + + final Hidden id = new Hidden(CONTENT_ITEM_ID); + add(id); + + // JavaScript auto-name generation is off by default. + // It is turned on under the following circumstances + // + // * If the name is null, upon starting edit of the title + // * If the name is null, upon finishing edit of name + // + // The rationale is that, auto-name generation is useful + // if the name is currently null, but once a name has been + // created you don't want to subsequently change it since + // it breaks URLs & potentially overwrites the user's + // customizations. + final TextField titleWidget = new TextField(new TrimmedStringParameter( + TITLE)); + titleWidget.setLabel(getTitleLabel()); + titleWidget.setHint(getTitleHint()); + titleWidget.addValidationListener(new NotNullValidationListener()); + titleWidget.setOnFocus("if (this.form." + NAME + ".value == '') { " + + " defaulting = true; this.form." + NAME + + ".value = urlize(this.value); }"); + titleWidget.setOnKeyUp( + "if (defaulting) { this.form." + NAME + + ".value = urlize(this.value) }"); + add(titleWidget); + + // For some content types it may be useful to change the label of + // the name (or URL) field to something different than 'name (url)'. + // This can now be accomplished by overwriting the getNameLabel() method. + // (jensp 2011-01-28) +// add(new Label(getNameLabel())); + final TextField nameWidget = new TextField(new TrimmedStringParameter( + NAME)); + nameWidget.setLabel(getNameLabel()); + nameWidget.setHint(getNameHint()); + // We just check parameter specific properties here! Additionally, + // context properties as uniqueness in folder must be validated + // for the form es the whole (using the validate method required by + // implementing FormValidationListener in this form. + nameWidget.addValidationListener(new NotNullValidationListener()); + nameWidget.addValidationListener(new URLTokenValidationListener()); + nameWidget.setMaxLength(190); + nameWidget.setOnFocus("defaulting = false"); + nameWidget.setOnBlur( + "if (this.value == '') " + + "{ defaulting = true; this.value = urlize(this.form." + + TITLE + + ".value) } " + + " else { this.value = urlize(this.value); }"); + add(nameWidget); + + } + + @Override + public void generateXML(PageState ps, Element parent) { + m_script.generateXML(ps, parent); + super.generateXML(ps, parent); + } + + /** + * @return the item selection model used in this form + */ + public ItemSelectionModel getItemSelectionModel() { + return m_itemModel; + } + + /** + * @return the save/cancel section for this form + */ + public SaveCancelSection getSaveCancelSection() { + return m_saveCancelSection; + } + + /** + * Perform form initialization. Children should override this this method to + * pre-fill the widgets with data, instantiate the content item, etc. + * + * @param e + * @throws FormProcessException + */ + @Override + public abstract void init(FormSectionEvent e) throws FormProcessException; + + /** + * Process the form. Children have to override this method to save the + * user's changes to the database. + * + * @param e + * @throws FormProcessException + */ + @Override + public abstract void process(FormSectionEvent e) throws FormProcessException; + + /** + * Validate the form. Children have to override this method to provide + * context form validation, specifically name (url) uniqueness in a folder! + * + * @param e + * @throws com.arsdigita.bebop.FormProcessException + */ + @Override + public void validate(FormSectionEvent e) throws FormProcessException { + // do nothing + } + + /** + * Adds a component to this container. + * + * @param pc the component to add to this BasicPageForm + * + */ + @Override + public void add(Component pc) { + m_widgetSection.add(pc); + } + + /** + * Adds a component with the specified layout constraints to this container. + * Layout constraints are defined in each layout container as static ints. + * Use a bitwise OR to specify multiple constraints. + * + * @param pc the component to add to this container + * @param constraints layout constraints (a bitwise OR of static ints in the + * particular layout) + */ + @Override + public void add(Component pc, int constraints) { + m_widgetSection.add(pc, constraints); + } + + /** + * This method can be overridden to change the label of the title field. To + * change to label of the title field can be useful for some content types. + * For example, for an organization the label "Title" for the field may be + * confusing for the normal user. For such a content type, the label would + * be changed to something like "Name of the organization". + * + * @return (Content for the) Label for the title field as string + */ + protected GlobalizedMessage getTitleLabel() { + return new GlobalizedMessage("cms.contenttypes.ui.title", + CmsConstants.CMS_BUNDLE); + } + + /** + * Provides the text for the user hint providing some detailed information + * how to use this widget. This method can be overwritten to adjust the text + * for some content types. {@link #getTitleLabel()} + * + * @return + */ + protected GlobalizedMessage getTitleHint() { + return new GlobalizedMessage("cms.contenttypes.ui.title_hint", + CmsConstants.CMS_BUNDLE); + } + + /** + * This method does the same as {@link #getTitleLabel() } for the labe l of + * the name (URL) field. + * + * @return (Content for the) Label for the name field as string + */ + protected GlobalizedMessage getNameLabel() { + return new GlobalizedMessage("cms.contenttypes.ui.name", + CmsConstants.CMS_BUNDLE); + } + + /** + * Provides the text for the unser hint providing some detailed information + * how to use this widget. This method can be overwritten to adjust the text + * for some content types. {@link #getNameLabel()} + * + * @return + */ + protected GlobalizedMessage getNameHint() { + return new GlobalizedMessage("cms.contenttypes.ui.name_hint", + CmsConstants.CMS_BUNDLE); + } + + // ////////////////////////////////////////////////////////////////////// + // + // VALIDATION helper methods + // + // ////////////////////////////////////////////////////////////////////// + /** + * Ensure that the name of an item is unique within a folder. A "New item" + * form should call this method in the validation listener. + * + * @param parent the folder in which to check + * @param event the {@link FormSectionEvent} which was passed to the + * validation listener + */ +// public void validateNameUniqueness(Category parent, FormSectionEvent event) { +// +// FormData data = event.getFormData(); +// String newName = (String) data.get(NAME); +// +// validateNameUniqueness(parent, event, newName); +// } + /** + * + * @param parent + * @param event + * @param newName + */ +// public void validateNameUniqueness(Category parent, +// FormSectionEvent event, +// String newName) { +// +// final String ERR_MSG = "cms.ui.authoring.an_item_with_this_name_exists"; +// +// if (newName != null) { +// final String query = "com.arsdigita.cms.validateUniqueItemName"; +// DataQuery dq = SessionManager.getSession().retrieveQuery(query); +// dq.setParameter("parentId", parent.getID()); +// dq.setParameter("name", newName.toUpperCase()); +// FormData data = event.getFormData(); +// ParameterData name = data.getParameter(NAME); +// +// if (dq.size() > 0) { +// // Try to get a currently selected content item +// ContentItem item = null; +// if (getItemSelectionModel() != null) { +// item = (ContentItem) getItemSelectionModel() +// .getSelectedObject(event.getPageState()); +// } +// if (item == null) { // The content item being null +// // means it is a creation form. +// // Therefore finding any item of the same name is a fault. +// data.addError(globalize(ERR_MSG)); +// return; +// } else { +// // means we are in a edit form. +// // We need to add all of the items that are different +// // versions of this item to the list so that we do not mark +// // an error if those are the only problems. +// BigDecimal itemID = null; +// Collection list = getAllVersionIDs(item); +// while (dq.next()) { +// itemID = (BigDecimal) dq.get("itemID"); +// if (!list.contains(itemID)) { +// String[] itemObj = new String[1]; +// itemObj[0] = itemID.toString(); +// dq.close(); +// data.addError(globalize(ERR_MSG, itemObj)); +// return; +// } +// } +// } +// } +// } +// } + /** + * NOT USED ANYMORE. + * + * Ensure that the name of an item is unique within a category. This should + * only be called from the validation listener of an "edit" form. + * + * @param event the {@link FormSectionEvent} which was passed to the + * validation listener + * @param id The id of the item that is being checked. This must no be null. + * @return + * + * @throws FormProcessException if the folder already contains an item with + * the name the user provided on the input form. / public void + * validateNameUniquenessWithinCategory(FormSectionEvent event, BigDecimal + * id) throws FormProcessException { if (id == null) { s_log.warn("Trying to + * validation the name uniqueness without " + " a valid item is invalid. + * This method should only " + " be called in \"edit\" forms. The passed in + * id " + " was null."); return; } // now we check to make sure that the new + * name is valid // within every category that the item is mapped to // this + * is only necessary for category browsing FormData data = + * event.getFormData(); String url = (String) data.get(NAME); if (url == + * null) { return; } DataQuery query = + * SessionManager.getSession().retrieveQuery( + * "com.arsdigita.categorization.getAllItemURLsForCategoryFromItem"); + * query.setParameter("itemID", id); query.addEqualsFilter("lower(url)", + * url.toLowerCase()); if (query.size() > 0) { // we need to make sure that + * the conflicting item is not a // pending or live version of the same item + * BigDecimal itemID = null; + * + * ContentItem item = (ContentItem)getItemSelectionModel() + * .getSelectedObject(event.getPageState()); Collection list = + * getAllVersionIDs(item); try { while (query.next()) { itemID = + * (BigDecimal) query.get("itemID"); if (!list.contains(itemID)) { // + * StringBuffer buffer = new StringBuffer((String) GlobalizationUtil // + * .globalize("cms.ui.authoring.error_conflicts_with_this_url") // + * .localize()); // buffer.append(url); String[] urlObj=new String[1]; + * urlObj[0]=url; throw new FormProcessException( "Error: Conflict with url: + * "+url, globalize( "cms.ui.authoring.error_conflicts_with_this_url", + * urlObj) ); } } + * + * } finally { query.close(); } } } + */ + /** + * Helper Method for name uniqueness validation. + * + * @param item + * @return + */ +// public static Collection getAllVersionIDs(ContentItem item) { +// // we need to add all of the items that are different versions +// // of this item to the list so that we do not throw an error +// // if those are the only problems +// ArrayList list = new ArrayList(); +// list.add(item.getID()); +// ContentItem live = item.getLiveVersion(); +// if (live != null) { +// list.add(live.getID()); +// } +// ItemCollection collection = item.getPendingVersions(); +// while (collection.next()) { +// list.add(collection.getID()); +// } +// return list; +// } +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderEditor.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderEditor.java.off index afdf6aea9..3cb3a48c5 100755 --- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderEditor.java.off +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderEditor.java.off @@ -22,7 +22,7 @@ import com.arsdigita.bebop.FormData; import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.event.FormSectionEvent; -import com.arsdigita.cms.Folder; +import org.libreccm.categorization.Category; /** @@ -44,11 +44,13 @@ public class FolderEditor extends FolderForm { /** * Initialize the form with name & label of folder being edited. + * @param e + * @throws com.arsdigita.bebop.FormProcessException */ public void init(FormSectionEvent e) throws FormProcessException { PageState s = e.getPageState(); FormData data = e.getFormData(); - Folder folder = getCurrentFolder(s); + Category folder = getCurrentFolder(s); data.put(NAME, folder.getName()); data.put(TITLE, folder.getLabel()); } @@ -57,7 +59,7 @@ public class FolderEditor extends FolderForm { public void process(FormSectionEvent e) throws FormProcessException { PageState s = e.getPageState(); FormData data = e.getFormData(); - Folder folder = getCurrentFolder(s); + Category folder = getCurrentFolder(s); updateFolder(folder, (String)data.get(NAME), (String)data.get(TITLE)); } diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderForm.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderForm.java.off new file mode 100755 index 000000000..ebffaf580 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderForm.java.off @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.folder; + +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.ui.authoring.BasicItemForm; +import com.arsdigita.util.Assert; + +// ToDo: Form can't extend BasicItemForm longer because Folders are Categories +// and not items. These means that this form must become indepenent. The +// BasicItemForm may be used as an template for this form. +// - Extends FormSection +// - Fields for: +// - Name +// - Title/Displayname +// - Description +// - abstractCategory? +// - visible? +// - enabled? +// +// - Localizable fields for default language +// - FolderCreator#process method must take care or adding category to parent and order field +// - + +/** + * Class FolderForm implements the basic form for creating or editing folders. + * + * @author Jens Pelzetter + * @author Jon Orris + * + */ +public abstract class FolderForm extends BasicItemForm{ + + FolderSelectionModel m_currentFolder; + + /** + * Create a new folder form. + * + * @param name Name of the form + * @param folder SelectionModel containing the current folder being operated on. + * + * @pre name != null && folder != null + */ + public FolderForm(String name, FolderSelectionModel folder) { + super(name, new ItemSelectionModel(Folder.BASE_DATA_OBJECT_TYPE, Folder.BASE_DATA_OBJECT_TYPE, "fldr")); + m_currentFolder = folder; + } + + public void register(Page p) { + super.register(p); + p.addComponentStateParam(this, getItemSelectionModel().getStateParameter()); + } + + /** + * Returns true if the form submission was cancelled. + */ + public boolean isCancelled(PageState s) { + return getSaveCancelSection().getCancelButton().isSelected(s); + } + + /** + * Validates the form. Checks for name uniqueness. + */ + public void validate(FormSectionEvent e) throws FormProcessException { + Folder folder = (Folder) m_currentFolder.getSelectedObject(e.getPageState()); + Assert.exists(folder); + validateNameUniqueness(folder, e); + } + + /** + * Updates a folder with a new parent, name, and label. + * + * @param folder The folder to update + * @param parent The new parent folder. May be null. + * @param name The new name of the folder + * @param label The new label for the folder + */ + final protected void updateFolder(Folder folder, Folder parent, String name, String label) { + folder.setParent(parent); + updateFolder(folder, name, label); + } + + /** + * Updates a folder with a new name and label. + * + * @param folder The folder to update + * @param name The new name of the folder + * @param label The new label for the folder + */ + final protected void updateFolder(Folder folder, String name, String label) { + folder.setName(name); + folder.setLabel(label); + folder.save(); + + // also modify the live version of the folder, + // otherwise items within this folder will keep + // using the old URL, for example + Folder live = (Folder) folder.getLiveVersion(); + if (live != null) { + live.setName(name); + live.setLabel(label); + live.save(); + } + } + + /** + * Returns the current folder being operated on. + * + * @return The current folder + * + * @pre state != null + * @post return != null + */ + final protected Folder getCurrentFolder(PageState state) { + Folder folder = (Folder) m_currentFolder.getSelectedObject(state); + return folder; + } +}