CCM NG: Current status Admin UIs

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3691 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2015-10-14 16:57:07 +00:00
parent 63d2f6d51a
commit 01befa0c74
51 changed files with 9841 additions and 7 deletions

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2001-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.bebop;
import com.arsdigita.globalization.GlobalizedMessage;
/**
* A link that runs its action listeners when it is clicked. The target of the
* link is the {@link Page} in which the action link is contained.
*
* <p> Typically, an action link is used in the following way:
* <pre>
* ActionLink l = new ActionLink("Send email to everybody");
* l.addActionListener(new ActionListener() {
* public void actionPerformed(ActionEvent e) {
* System.out.println("Link was clicked.");
* ... figure out who everybody is and send them email ...
* }
* });
* </pre>
*
* <p>See {@link BaseLink} for a description of all Bebop Link classes
* and suggestions for using them.
*
* @author David Lutterkort
* @version $Id$ */
public class ActionLink extends ControlLink {
/**
* The value for the XML type attribute for an {@link ActionLink}.
*/
protected final String TYPE_ACTION = "action";
/**
* Constructs a new ActionLink. The link encapsulates
* the child component (usually either a label or an image).
*
* @param child the component to be turned into a link
*/
public ActionLink(Component child) {
super(child);
setTypeAttr(TYPE_ACTION);
}
/**
* Constructs a new ActionLink with the given string label.
*
* @param label the string label for the link
*/
public ActionLink(GlobalizedMessage label) {
this(new Label(label));
}
/**
* Constructs a new ActionLink with the given string label.
*
* @param label the string label for the link
* @deprecated refactor to use @see ActionLink(GlobalizedMessage label)
*/
public ActionLink(String label) {
this(new Label(label));
}
/**
* Sets the page state's control event. Should be overridden by child
* classes. By default, the link does not receive any control events.
*
* @param s the current page state
*/
@Override
public void setControlEvent(PageState s) {
s.setControlEvent(this);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2001-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.bebop;
import com.arsdigita.bebop.PageState;
/**
*
* Interface that should be implemented by components that have
* state parameters that need to be reset when the component is shown
* to the user. The details of when the reset method is called are left
* to the application programmer.
*
* @version $Id$
*/
public interface Resettable {
/**
* Resets the state of the component to its original
* appearance.
*
* @param state the page state
*/
void reset(PageState state);
}

View File

@ -0,0 +1,251 @@
/*
* Copyright (C) 2001-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.bebop;
import static com.arsdigita.bebop.Component.*;
import static com.arsdigita.bebop.util.BebopConstants.*;
import com.arsdigita.xml.Element;
import com.arsdigita.util.Assert;
import com.arsdigita.bebop.util.BebopConstants;
/**
* Generates a list of segments. Each segment consists of a header
* (which could be any Bebop component) and a body (which, likewise,
* could be any component). The entire <code>SegmentedPanel</code>
* look roughly like this:
*
* <blockquote><pre><code>
* ----------------------
* Header 1
* ----------------------
* Body 1
* More Body 1
* Even more Body 1
* ----------------------
* Header 2
* ----------------------
* Body 2
* More Body 2
* Even more Body 2
* </code></pre></blockquote>
*
* Typically, the body of each segment will be a {@link SimpleContainer}
* which contains many other components
* <p>
* The XML generated by this component looks something like this:
* <blockquote><pre><code>
* &lt;bebop:segmentedPanel&gt;
* &lt;bebop:segment id="foo"&gt;
* &lt;bebop:segmentHeader&gt;
* &lt;aRandomHeaderComponent/&gt;
* &lt;anotherRandomHeaderComponent/&gt;
* ...
* &lt;/bebop:segmentHeader&gt;
* &lt;bebop:segmentBody&gt;
* &lt;aRandomBodyComponent&gt;
* &lt;anotherRandomBodyComponent&gt;
* ...
* &lt;/bebop:segmentBody&gt;
* &lt;/bebop:segment&gt;
* ...
* &lt;/bebop:segmentedPanel&gt;
* </code></pre></blockquote>
*
* @see #generateXML(PageState, Element)
*
* @author Michael Pih
* @version $Id$
*/
public class SegmentedPanel extends SimpleContainer
implements BebopConstants {
public static final String HEADER_CLASS = "seg-header";
/**
* Construct a new <code>SegmentedPanel</code>
*/
public SegmentedPanel() {
super();
}
/**
* Construct a new <code>SegmentedPanel</code>
*
* @param idAttr the XSL ID attribute for this container
* @see SimpleComponent#setIdAttr(String)
*/
public SegmentedPanel(String idAttr) {
this();
setIdAttr(idAttr);
}
/**
* Add a segment to this container
*
* @param header the component that will act as the header
* @param body the component that will act as the body
* @return the new segment
*/
public Segment addSegment(Component header, Component body) {
Segment s = new Segment(header, body);
add(s);
return s;
}
/**
* Add a segment to this container.
*
* @param segmentID the XSL ID attribute for the new segment.
* @param header the component that will act as the header.
* @param body the component that will act as the body
* @return the new segment
*/
public Segment addSegment(String segmentID, Component header, Component body) {
Segment s = addSegment(header, body);
s.setIdAttr(segmentID);
return s;
}
/**
* Add a segment to this container.
*
* @param segmentID the XSL ID attribute for the new segment. The XSL template
* for this component will render the correct header based on the ID attribute
* @param body the component that will act as the body
* @return the new segment
*/
public Segment addSegment(String segmentID, Component body) {
return addSegment(segmentID, null, body);
}
/**
* Add and return a new empty segment.
*
* @return a new empty segment that is part of the panel.
*/
public Segment addSegment() {
Segment result = new Segment();
add(result);
return result;
}
/**
* Generate the XML for this component, as described above
*
* @param state represents the page state for the current request
* @param parent the parent XML element
*/
public void generateXML(PageState state, Element parent) {
if ( isVisible(state) ) {
Element panel = parent.newChildElement(BEBOP_SEG_PANEL, BEBOP_XML_NS);
exportAttributes(panel);
super.generateXML(state, panel);
}
}
/**
* A single Segment within this container
*/
public static class Segment extends SimpleContainer {
private SimpleContainer m_header, m_body;
/**
* Construct an empty <code>Segment</code>
*/
public Segment() {
this(null, null);
}
/**
* Construct a new <code>Segment</code>
*
* @param header the component which will act as the header; the XSL
* class attribute for the component will be set to {@link #HEADER_CLASS}.
* Typically, this component will be a {@link Label}
* @param body the component which represents the body of the segment, Typically,
* this component will be a {@link SimpleContainer} or a panel of some sort
*/
public Segment(Component header, Component body) {
super();
if(header != null) addHeader(header);
if(body!= null) add(body);
}
/**
* Construct a new <code>Segment</code> with no header
*
* @param body the component which represents the body of the segment, Typically,
* this component will be a {@link SimpleContainer} or a panel of some sort
*/
public Segment(Component body) {
this(null, body);
}
/**
* Add a header component.
*
* @param c an additional header component
*/
public void addHeader(Component c) {
Assert.isUnlocked(this);
if(m_header == null) {
m_header = new SimpleContainer(BEBOP_SEG_HEADER, BEBOP_XML_NS);
super.add(m_header);
}
m_header.add(c);
}
/**
* Add a component to the body of this segment
*/
public void add(Component c) {
Assert.isUnlocked(this);
if(m_body == null) {
m_body = new SimpleContainer(BEBOP_SEG_BODY, BEBOP_XML_NS);
super.add(m_body);
}
m_body.add(c);
}
/**
* Add a component to the body of this segment
*/
public void add(Component c, int constraints) {
add(c);
}
/**
* Generate the XML for this segment
*
* @param state the current page state
* @param parent the parent XML element
*/
public void generateXML(PageState state, Element parent) {
if(isVisible(state)) {
Element seg = parent.newChildElement(BEBOP_SEGMENT, BEBOP_XML_NS);
exportAttributes(seg);
super.generateXML(state, seg);
}
}
}
}

View File

@ -0,0 +1,498 @@
/*
* Copyright (C) 2001-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.bebop;
import static com.arsdigita.bebop.Component.*;
import com.arsdigita.bebop.parameters.IntegerParameter;
import com.arsdigita.util.Assert;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.xml.Element;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import javax.servlet.ServletException;
import org.apache.log4j.Logger;
/* FIXME: Add methods for using images in the tab strip */
/**
* A tabbed pane that lets the user switch between components by
* clicking on a given title in the tab strip.
* <p>
* Tabs (components) are added using the {@link #addTab addTab} method. Each
* entry consists of a label (which is a string) and the {@link Component}
* that is displayed if the user clicks on the label.
* <p>
* There is always exactly one component that is currently visible, the component
* that is returned by {@link #getCurrentPane}. Without user interaction,
* this is the default pane -- that was set by {@link #setDefaultPane} -- or, if
* none has been set, the first component that was added to the <code>TabbedPane</code>.
* <p>
*
* @author David Lutterkort
* @author Stanislav Freidin
* @author Uday Mathur
* @version $Id$
*/
public class TabbedPane extends SimpleContainer {
private static final String CURRENT_PANE = "pane";
/**
* The name for the event to change the selected pane.
* The value is the index of the pane
*/
private static final String SELECT_EVENT = "select";
private Pane m_defaultPane;
private IntegerParameter m_currentPaneParam;
private List m_actionListeners;
private static final Logger s_log =
Logger.getLogger(TabbedPane.class.getName());
/**
* Constructs an empty TabbedPane.
*/
public TabbedPane() {
m_currentPaneParam = new IntegerParameter(CURRENT_PANE);
}
/**
* Registers with the specified root container. Adds a state
* parameter to keep track of the visible component to the page.
* @param p the root container to register with
* @pre p != null
*/
public void register(Page p) {
Assert.isUnlocked(this);
p.addComponentStateParam(this, m_currentPaneParam);
// if there is no default pane, then set it to the first one
// in the list
Iterator i = children();
if (!i.hasNext()) {
s_log.warn("TabbedPane registered with no panes");
} else if (m_defaultPane == null) {
setDefaultPaneIndex(0);
}
while (i.hasNext()) {
Pane pane = (Pane) i.next();
p.setVisibleDefault(pane.getComponent(), pane == m_defaultPane);
}
}
/**
* Adds a new pane to the dialog. Assigns a rather unhelpful default label
* (the pane number) to the component. Use {@link #addTab addTab}
* instead.
*
* @pre pc != null
*/
public void add(Component pc) {
addTab(String.valueOf(size()), pc);
}
/**
* Adds a new pane with layout constraints to the dialog. Ignores
* the constraints. Assigns a rather unhelpful default label
* (the pane number) to the component. Use {@link #addTab
* addTab} instead.
*
* @pre pc != null */
public void add(Component pc, int constraints) {
add(pc);
}
/**
* Adds a tab and its associated component.
* @param label the text to display in the tab strip
* @param c the component to display when the user clicks on the
* <code>label</code> in the tab strip
*
* @pre label != null && c != null
*/
public void addTab(Component label, Component c) {
Assert.isUnlocked(this);
super.add(new Pane(label, c));
}
/**
* Adds a tab and its associated component.
* @param label the text to display in the tab strip
* @param c the component to display when the user clicks on the
* <code>label</code> in the tab strip
*
* @pre label != null && c != null
*/
public void addTab(String label, Component c) {
addTab(new Label(label), c);
}
/**
* Adds an <code>ActionListener</code>, which is run whenever {@link
* #respond respond} is called.
* @param 1 the action listener
*
* @pre l != null
* @pre ! isLocked()
* @see #respond respond
*/
public void addActionListener(ActionListener l) {
Assert.isUnlocked(this);
if ( m_actionListeners == null ) {
m_actionListeners = new ArrayList();
}
m_actionListeners.add(l);
}
/**
* Removes a previously added <code>ActionListener</code>.
* @param 1 the action listener to remove
* @see #addActionListener addActionListener
*/
public void removeActionListener(ActionListener l) {
Assert.isUnlocked(this);
if ( m_actionListeners == null ) {
return;
}
m_actionListeners.remove(l);
}
/**
* Fires an <code>ActionEvent</code>. All registered
* <code>ActionListener</code>s are run. The source of the event is the
* <code>TabbedPane</code>.
* @param state the current page state
* @pre state != null
* @see #respond respond
*/
protected void fireActionEvent(PageState state) {
ActionEvent e = null;
if (m_actionListeners == null) {
return;
}
for (Iterator i=m_actionListeners.iterator(); i.hasNext(); ) {
if ( e == null ) {
e = new ActionEvent(this, state);
}
((ActionListener) i.next()).actionPerformed(e);
}
}
/**
* Sets the index of the default pane, which is visible until the user
* clicks on another label in the tab strip.
* @param i the index of the default pane
*/
protected void setDefaultPaneIndex(int i) {
m_currentPaneParam.setDefaultValue(new Integer(i));
m_defaultPane = (Pane)get(i);
}
/**
* Sets the default pane, which is visible until the user
* clicks on another label in the tab strip.
* @param pane the component to display as the default pane
*
* @pre findPane(pane) != -1
*/
public void setDefaultPane(Component pane)
throws IllegalArgumentException {
Assert.isUnlocked(this);
setDefaultPaneIndex(findPaneSafe(pane));
}
/**
* Show or hide a particular tab
*
* @param s the page state
* @param i the index of the tab
* @param v if true, shows the tab. Otherwise, hides the tab
*/
public void setTabVisible(PageState s, int i, boolean v) {
get(i).setVisible(s, v);
}
/**
* Show or hide a particular tab
*
* @param s the page state
* @param c the body of the tab
* @param v if true, shows the tab. Otherwise, hides the tab
*/
public void setTabVisible(PageState s, Component c, boolean v) {
int i = findPaneSafe(c);
setTabVisible(s, i, v);
}
/**
* Determine if a particular tab is visible
*
* @param s the page state
* @param i the index of the tab
*/
public boolean isTabVisible(PageState s, int i) {
return get(i).isVisible(s);
}
/**
* Determine if a particular tab is visible
*
* @param s the page state
* @param c the body of the tab
*/
public boolean isTabVisible(PageState s, Component c) {
int i = findPaneSafe(c);
return isTabVisible(s, i);
}
/**
* Find the pane whose body is the specified component
* @param c the component
* @return the pane index on success, -1 if no such pane exists
*/
protected int findPane(Component c) {
int index = 0;
for(Iterator i = children(); i.hasNext(); index++) {
Pane p = (Pane)i.next();
if(p.getComponent() == c)
return index;
}
return -1;
}
private int findPaneSafe(Component c) {
int i = findPane(c);
if ( i == -1 ) {
throw new IllegalArgumentException
("Pane not part of this tabbed dialog");
}
return i;
}
/**
* Gets the default pane. If no default pane has been set explicitly, the
* first pane is returned.
*
* @return the default pane, or <code>null</code> if there are no
* panes.
*/
public Component getDefaultPane() {
return m_defaultPane.getComponent();
}
/**
* Gets the pane with the specified label.
* @return the pane with the specified label, or <code>null</code>
* if a pane with that label does not exist.
*/
public Component getPane(Component label) {
for (Iterator i = children(); i.hasNext();) {
Pane p = (Pane) i.next();
if ( p.getLabel().equals(label) ) {
return p.getComponent();
}
}
return null;
}
/**
* Gets the pane with the specified key in its label.
* Returns null if a pane with that label does not exist.
* This function exists for backward compatibility.
* @return the pane with the specified label, or <code>null</code>
* if a pane with that label does not exist.
*/
public Component getPane(String label) {
for (Iterator i = children(); i.hasNext();) {
Pane p = (Pane) i.next();
Component pLabel = p.getLabel();
if (pLabel instanceof Label
&& ((Label)pLabel).getLabel().equals(label) ) {
return p.getComponent();
}
}
return null;
}
/**
* Gets the currently visible pane.
*
* @pre data != null
*/
public Component getCurrentPane(PageState data) {
return getCurrent(data).getComponent();
}
/**
* Get the currently visible <code>Pane</code>, the tab label together
* with its component.
*/
private Pane getCurrent(PageState data) {
Integer i = (Integer) data.getValue(m_currentPaneParam);
if (i == null) {
if (m_defaultPane!=null) {
return m_defaultPane;
} else {
return (Pane)get(0);
}
}
return (Pane)get(i.intValue());
}
public void setSelectedIndex(PageState state, int index) {
if ( index != getSelectedIndex(state) ) {
getCurrentPane(state).setVisible(state, false);
state.setValue(m_currentPaneParam, new Integer(index));
getCurrentPane(state).setVisible(state, true);
}
}
public int getSelectedIndex(PageState state) {
Integer current = (Integer) state.getValue(m_currentPaneParam);
if ( current == null ) {
return -1;
}
return current.intValue();
}
/**
* Builds a DOM representing the header for the tab strip. Marks the current pane.
*/
protected void generateTabs(PageState data, Element parent) {
Element strip = parent.newChildElement("bebop:tabStrip", BEBOP_XML_NS);
exportAttributes(strip);
Pane current = getCurrent(data);
strip.addAttribute("selected",current.getComponent().getClass().getName());
Iterator tabs;
int i;
for (tabs = children(), i = 0; tabs.hasNext(); i++) {
Pane pane = (Pane)tabs.next();
// Skip hidden tabs
if(!pane.isVisible(data)) continue;
data.setControlEvent(this, SELECT_EVENT, String.valueOf(i));
Element tab = strip.newChildElement("bebop:tab", BEBOP_XML_NS);
if (pane == current) {
tab.addAttribute("current", "t");
} else {
try {
tab.addAttribute("href", data.stateAsURL());
} catch (java.io.IOException ioe) {
// stateAsURL failed => this node gets neither href nor current
//TODO cat.error("cannot get stateAsURL from "+data);
}
}
String key = ((Label) pane.getLabel()).getGlobalizedMessage().getKey();
tab.addAttribute("key", key.substring(key.lastIndexOf(".") + 1));
pane.getLabel().generateXML(data, tab);
}
data.clearControlEvent();
}
/**
* Services the request by building a DOM tree with the tabs
* themselves and then the included page.
* <p>Generates a DOM fragment:
* <p><code><pre>
* &lt;bebop:tabbedPane>
* &lt;bebop:tabStrip>
* &lt;bebop:tab [href="..."] [current="t|f"]> .. label .. &lt;/bebop:tab>
* &lt;bebop:tab [href="..."] [current="t|f"]> .. label .. &lt;/bebop:tab>
* &lt;bebop:tab [href="..."] [current="t|f"]> .. label .. &lt;/bebop:tab>
* &lt;/bebop:tabStrip>
* &lt;bebop:currentPane>
* ... contentes ..
* &lt;/bebop:currentPane>
* &lt;/bebop:tabbedPane>
* </pre></code>
*/
public void generateXML(PageState state, Element parent) {
if ( isVisible(state) && !isEmpty()) {
Element tabbed = parent.newChildElement("bebop:tabbedPane", BEBOP_XML_NS);
generateTabs(state, tabbed);
exportAttributes(tabbed);
Element pane = tabbed.newChildElement("bebop:currentPane", BEBOP_XML_NS);
exportAttributes(pane);
getCurrentPane(state).generateXML(state, pane);
}
}
/**
* Notifies the <code>TabbedPane</code> that one of the tabs has been
* selected. Changes the currently visible pane and runs all the {@link
* ActionListener ActionListeners}.
* <p>
* The <code>respond</code> method on the now-visible component is
* <em>not</em> called.
*
* @pre state != null
*/
public void respond(PageState state)
throws ServletException
{
String event = state.getControlEventName();
if ( SELECT_EVENT.equals(event)) {
String value = state.getControlEventValue();
setSelectedIndex(state, Integer.parseInt(value));
} else {
throw new ServletException("Received unknown control event " + event);
}
fireActionEvent(state);
}
/**
* Associates a label with the component
*/
private class Pane extends SimpleContainer {
private Component m_label;
private Component m_component;
public Pane(Component label, Component c) {
m_label = label;
super.add(label);
m_component = c;
super.add(c);
}
public final Component getLabel() {
return m_label;
}
public final Component getComponent() {
return m_component;
}
}
}

View File

@ -0,0 +1,896 @@
/*
* Copyright (C) 2001-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.bebop;
import static com.arsdigita.bebop.Component.*;
import com.arsdigita.bebop.event.EventListenerList;
import com.arsdigita.bebop.event.TableActionAdapter;
import com.arsdigita.bebop.event.TableActionEvent;
import com.arsdigita.bebop.event.TableActionListener;
import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.table.AbstractTableModelBuilder;
import com.arsdigita.bebop.table.DefaultTableCellRenderer;
import com.arsdigita.bebop.table.DefaultTableColumnModel;
import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableColumn;
import com.arsdigita.bebop.table.TableColumnModel;
import com.arsdigita.bebop.table.TableHeader;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.bebop.table.TableModelBuilder;
import static com.arsdigita.bebop.util.BebopConstants.*;
import com.arsdigita.bebop.util.BebopConstants;
import com.arsdigita.util.Assert;
import com.arsdigita.xml.Element;
import java.util.Iterator;
import javax.servlet.ServletException;
import org.apache.log4j.Logger;
/**
* Displays statically or dynamically generated data in tabular form.
* The data is retrieved from a <code>TableModel</code>.
*
* <p>
* This class is similar to the {@link List} class, but it has two dimensions.
* The table consists of a {@link TableModelBuilder}, a {@link TableColumnModel},
* a {@link TableHeader} and a {@link TableCellRenderer} for each column.
* <p>
*
* A table that represents a static matrix can be created fairly quickly:
* <blockquote><pre><code> String[][] data = {
* {"Stas", "Freidin"},
* {"David", "Lutterkort"}
* };
*
* String[] headers = {"First Name", "Last Name"};
*
* Table myTable = new Table(data, headers);</code></pre></blockquote>
* <p>
*
* However, tables are most often used to represent database queries, not static
* data. For these tables, the {@link TableModelBuilder} class should be used
* to supply the <code>Table</code> class with data.
* The {@link TableModelBuilder} class will execute the database query and
* return a {@link TableModel}, which wraps the query.
* <p>
*
* The content in the cells is rendered by the {@link TableCellRenderer} that
* is set for the {@link TableColumn} to which the cell belongs.
*
* If the <code>TableCellRenderer</code> has not been set, the
* <code>TableCellRenderer</code> for the <code>Table</code> is used.
* By default, the <code>Table</code> class uses an inactive instance of the
* {@link DefaultTableCellRenderer} (cell content is displayed as {@link Label}s).
* However, if an active <code>DefaultTableCellRenderer</code> is used, the
* cells in the table appear as links. When the user clicks a link, the
* <code>Table</code>'s action listeners will be fired.
*
* <P>
* The currently selected cell is represented by two {@link SingleSelectionModel}s -
* one model for the row and one model for the column. Typically, the selected
* row is identified by a string key and the selected column is identified by
* an integer.
*
* @see TableModel
* @see TableColumnModel
*
* @author David Lutterkort
* @version $Id$
*/
public class Table extends SimpleComponent {
private static final Logger logger = Logger.getLogger(Table.class);
// Names for HTML Attributes
private static final String WIDTH = "width";
private static final String CELL_SPACING = "cellspacing";
private static final String CELL_PADDING = "cellpadding";
private static final String BORDER = "border";
private static final String SELECTED_ROW = "row";
/**
* The control event when the user selects one table cell.
* This control event will only be used when
*/
protected static final String CELL_EVENT = "cell";
protected static final char SEP = ' ';
private TableModelBuilder m_modelBuilder;
private TableColumnModel m_columnModel;
private TableHeader m_header;
private RequestLocal m_tableModel;
private SingleSelectionModel m_rowSelectionModel;
/**
* A listener to forward headSelected events originating from the
* TableHeader. This will be null until somebody actually registers a
* TableActionListener from the outside.
*/
private TableActionListener m_headerForward;
private EventListenerList m_listeners;
private TableCellRenderer m_defaultCellRenderer;
private Component m_emptyView;
private boolean m_striped = false;
/**
* Constructs a new, empty table.
*/
public Table() {
this(new Object[0][0], new Object[0]);
}
/**
* Constructs a static table with the specified column headers,
* and pre-fills it with data.
*
* @param data a matrix of objects that will serve as static data
* for the table cells
*
* @param headers an array of string labels for the table headers
*/
public Table(Object[][] data, Object[] headers) {
this(new MatrixTableModelBuilder(data), headers);
}
/**
* Constructs a table using a {@link TableModelBuilder}. The table
* data will be generated dynamically during each request.
*
* @param b the {@link TableModelBuilder} that is responsible for
* instantiating a {@link TableModel} during each request
*
* @param headers an array of string labels for the table headers
*/
public Table(TableModelBuilder b, Object[] headers) {
this(b, new DefaultTableColumnModel(headers));
}
/**
* Constructs a table using a {@link TableModelBuilder}. The table
* data will be generated dynamically during each request. The
* table's columns and headers will be provided by a
* {@link TableColumnModel}.
*
* @param b the {@link TableModelBuilder} that is responsible for
* instantiating a {@link TableModel} during each request
*
* @param c the {@link TableColumnModel} that will maintain the
* columns and headers for this table
*/
public Table(TableModelBuilder b, TableColumnModel c) {
super();
m_modelBuilder = b;
m_columnModel = c;
setHeader(new TableHeader(m_columnModel));
m_rowSelectionModel =
new ParameterSingleSelectionModel(new StringParameter(SELECTED_ROW));
m_listeners = new EventListenerList();
m_defaultCellRenderer = new DefaultTableCellRenderer();
initTableModel();
}
// Events and listeners
/**
* Adds a {@link TableActionListener} to the table. The listener is
* fired whenever a table cell is clicked.
*
* @param l the {@link TableActionListener} to be added
*/
public void addTableActionListener(TableActionListener l) {
Assert.isUnlocked(this);
if (m_headerForward == null) {
m_headerForward = createTableActionListener();
if (m_header != null) {
m_header.addTableActionListener(m_headerForward);
}
}
m_listeners.add(TableActionListener.class, l);
}
/**
* Removes a {@link TableActionListener} from the table.
*
* @param l the {@link TableActionListener} to be removed
*/
public void removeTableActionListener(TableActionListener l) {
Assert.isUnlocked(this);
m_listeners.remove(TableActionListener.class, l);
}
/**
* Fires event listeners to indicate that a new cell has been
* selected in the table.
*
* @param state the page state
* @param rowKey the key that identifies the selected row
* @param column the integer index of the selected column
*/
protected void fireCellSelected(PageState state,
Object rowKey, Integer column) {
Iterator i = m_listeners.getListenerIterator(TableActionListener.class);
TableActionEvent e = null;
while (i.hasNext()) {
if (e == null) {
e = new TableActionEvent(this, state, rowKey, column);
}
((TableActionListener) i.next()).cellSelected(e);
}
}
/**
* Fires event listeners to indicate that a new header cell has been
* selected in the table.
*
* @param state the page state
* @param rowKey the key that identifies the selected row
* @param column the integer index of the selected column
*/
protected void fireHeadSelected(PageState state,
Object rowKey, Integer column) {
Iterator i = m_listeners.getListenerIterator(TableActionListener.class);
TableActionEvent e = null;
while (i.hasNext()) {
if (e == null) {
e = new TableActionEvent(this, state, rowKey, column);
}
((TableActionListener) i.next()).headSelected(e);
}
}
/**
* Instantiates a new {@link TableActionListener} for this table.
*
* @return a new {@link TableActionListener} that should be used
* only for this table.
*
*/
protected TableActionListener createTableActionListener() {
return new TableActionAdapter() {
@Override
public void headSelected(TableActionEvent e) {
fireHeadSelected(e.getPageState(), e.getRowKey(), e.getColumn());
}
};
}
/**
* @return the {@link TableColumnModel} for this table.
*/
public final TableColumnModel getColumnModel() {
return m_columnModel;
}
/**
* Sets a new {@link TableColumnModel} for the table.
*
* @param v the new {@link TableColumnModel}
*/
public void setColumnModel(TableColumnModel v) {
Assert.isUnlocked(this);
m_columnModel = v;
}
/**
* @return the {@link TableModelBuilder} for this table.
*/
public final TableModelBuilder getModelBuilder() {
return m_modelBuilder;
}
/**
* Sets a new {@link TableModelBuilder} for the table.
*
* @param v the new {@link TableModelBuilder}
*/
public void setModelBuilder(TableModelBuilder v) {
Assert.isUnlocked(this);
m_modelBuilder = v;
}
/**
* @return the {@link TableHeader} for this table. Could return null
* if the header is hidden.
*/
public final TableHeader getHeader() {
return m_header;
}
/**
* Sets a new header for this table.
*
* @param v the new header for this table. If null, the header will be
* hidden.
*/
public void setHeader(TableHeader v) {
Assert.isUnlocked(this);
if (m_headerForward != null) {
if (m_header != null) {
m_header.removeTableActionListener(m_headerForward);
}
if (v != null) {
v.addTableActionListener(m_headerForward);
}
}
m_header = v;
if (m_header != null) {
m_header.setTable(this);
}
}
/**
* @param i the numerical index of the column
* @return the {@link TableColumn} whose index is i.
*/
public TableColumn getColumn(int i) {
return getColumnModel().get(i);
}
/**
* Maps the colulumn at a new numerical index. This method
* is normally used to rearrange the order of the columns in the
* table.
*
* @param i the numerical index of the column
* @param v the column that is to be mapped at i
*/
public void setColumn(int i, TableColumn v) {
getColumnModel().set(i, v);
}
/**
* @return the {@link SingleSelectionModel} that is responsible
* for selecting the current row.
*/
public final SingleSelectionModel getRowSelectionModel() {
return m_rowSelectionModel;
}
/**
* Specifies the {@link SingleSelectionModel} that will be responsible
* for selecting the current row.
*
* @param v a {@link SingleSelectionModel}
*/
public void setRowSelectionModel(SingleSelectionModel v) {
Assert.isUnlocked(this);
m_rowSelectionModel = v;
}
/**
* @return the {@link SingleSelectionModel} that is responsible
* for selecting the current column.
*/
public SingleSelectionModel getColumnSelectionModel() {
return (getColumnModel() == null) ? null : getColumnModel().
getSelectionModel();
}
/**
* Specifies the {@link SingleSelectionModel} that will be responsible
* for selecting the current column.
*
* @param v a {@link SingleSelectionModel}
*/
public void setColumnSelectionModel(SingleSelectionModel v) {
Assert.isUnlocked(this);
// TODO: make sure table gets notified of changes
getColumnModel().setSelectionModel(v);
}
/**
* Clears the row and column selection models that the table holds.
*
* @param s represents the state of the current request
* @post ! getRowSelectionModel().isSelected(s)
* @post ! getColumnSelectionModel().isSelected(s)
*/
public void clearSelection(PageState s) {
getRowSelectionModel().clearSelection(s);
getColumnSelectionModel().clearSelection(s);
}
/**
* @return the default {@link TableCellRenderer}.
*/
public final TableCellRenderer getDefaultCellRenderer() {
return m_defaultCellRenderer;
}
/**
* Specifies the default cell renderer. This renderer will
* be used to render columns that do not specify their own
* {@link TableCellRenderer}.
*
* @param v the default {@link TableCellRenderer}
*/
public final void setDefaultCellRenderer(TableCellRenderer v) {
m_defaultCellRenderer = v;
}
/**
* @return the component that will be shown if the table is
* empty.
*/
public final Component getEmptyView() {
return m_emptyView;
}
/**
* Sets the empty view. The empty view is the component that
* is shown if the table is empty. Usually, the component
* will be a simple label, such as <code>new Label("The table is empty")</code>.
*
* @param v a Bebop component
*/
public final void setEmptyView(Component v) {
m_emptyView = v;
}
// Set HTML table attributes
/**
*
* @return the HTML width of the table.
*/
public String getWidth() {
return getAttribute(WIDTH);
}
/**
*
* @param v the HTML width of the table
*/
public void setWidth(String v) {
setAttribute(WIDTH, v);
}
/**
*
* @return the HTML border of the table.
*/
public String getBorder() {
return getAttribute(BORDER);
}
/**
*
* @param v the HTML border of the table
*/
public void setBorder(String v) {
setAttribute(BORDER, v);
}
public String getCellSpacing() {
return getAttribute(CELL_SPACING);
}
/**
*
* @param v the HTML width of the table
*/
public void setCellSpacing(String v) {
setAttribute(CELL_SPACING, v);
}
/**
*
* @return the HTML cell spacing of the table.
*/
public String getCellPadding() {
return getAttribute(CELL_PADDING);
}
/**
*
* @param v the HTML cell padding of the table
*/
public void setCellPadding(String v) {
setAttribute(CELL_PADDING, v);
}
/**
* Processes the events for this table. This method will automatically
* handle all user input to the table.
*
* @param s the page state
* @throws javax.servlet.ServletException
*/
@Override
public void respond(PageState s) throws ServletException {
String event = s.getControlEventName();
String rowKey = null;
Integer column = null;
if (CELL_EVENT.equals(event)) {
String value = s.getControlEventValue();
SingleSelectionModel rowSel = getRowSelectionModel();
SingleSelectionModel colSel = getColumnSelectionModel();
int split = value.indexOf(SEP);
rowKey = value.substring(0, split);
column = new Integer(value.substring(split + 1));
colSel.setSelectedKey(s, column);
rowSel.setSelectedKey(s, rowKey);
fireCellSelected(s, rowKey, column);
} else {
throw new ServletException("Unknown event '" + event + "'");
}
}
/**
* Registers the table with the containing page. The table will add the
* state parameters of the row and column selection models, if they use
* them, thus making the selection persist between requests.
*
* @param p the page that contains this table
*/
@Override
public void register(Page p) {
ParameterModel m = getRowSelectionModel() == null ? null
: getRowSelectionModel().getStateParameter();
if (m != null) {
p.addComponentStateParam(this, m);
}
m = getColumnSelectionModel() == null ? null : getColumnSelectionModel().
getStateParameter();
if (m != null) {
p.addComponentStateParam(this, m);
}
}
/**
* Returns an iterator over the header and all the columns. If the table
* has no header, the iterator lists only the columns.
*
* @return an iterator over Bebop components.
*/
@Override
public Iterator children() {
return new Iterator() {
int pos = (getHeader() == null) ? -1 : -2;
@Override
public boolean hasNext() {
return pos < getColumnModel().size() - 1;
}
@Override
public Object next() {
pos += 1;
if (pos == -1) {
return getHeader();
} else {
return getColumn(pos);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("Read-only iterator.");
}
};
}
/**
* Determines whether a row is seleted.
*
* @param s the page state
* @param rowKey the key that identifies the row
* @return <code>true</code> if the row is currently selected;
* <code>false</code> otherwise.
*/
public boolean isSelectedRow(PageState s, Object rowKey) {
if (rowKey == null || getRowSelectionModel() == null) {
return false;
}
return getRowSelectionModel().isSelected(s)
&& rowKey.toString().equals(
getRowSelectionModel().getSelectedKey(s).toString());
}
/**
* Determines whether a column is selected.
*
* @param s the page state
* @param column a key that identifes the column. Should be consistent
* with the type used by the column selection model.
* @return <code>true</code> if the column is selected;
* <code>false</code> otherwise.
*/
public boolean isSelectedColumn(PageState s, Object column) {
if (column == null || getColumnSelectionModel() == null) {
return false;
}
return getColumnSelectionModel().isSelected(s)
&& column.toString().equals(
getColumnSelectionModel().getSelectedKey(s).toString());
}
/**
* Determines whether the cell addressed by the specified row key and
* column number is selected in the request represented by the page
* state.
*
* @param s represents the state of the page in the current request
* @param rowKey the row key of the cell. The concrete type should agree
* with the type used by the row selection model.
* @param column the column of the cell. The concrete type should agree
* with the type used by the column selection model.
* @return <code>true</code> if the cell is selected;
* <code>false</code> otherwise.
*/
public boolean isSelectedCell(PageState s, Object rowKey, Object column) {
return isSelectedRow(s, rowKey) && isSelectedColumn(s, column);
}
public void setStriped(boolean striped) {
m_striped = striped;
}
public boolean getStriped() {
return m_striped;
}
/**
* Adds type-specific XML attributes to the XML element representing
* this link. Subclasses should override this method if they introduce
* more attributes than the ones {@link #generateXML generateXML}
* produces by default.
*
* @param state represents the current request
* @param element the XML element representing this table
*/
protected void generateExtraXMLAttributes(PageState state,
Element element) {
}
/**
* Generates the XML representing the table. Gets a new {@link TableModel}
* from the {@link TableModelBuilder} and iterates over the model's
* rows. The value in each table cell is rendered with the help of the
* column's table cell renderer.
*
* <p> Generates an XML fragment:
* <pre>
* &lt;bebop:table&gt;
* &lt;bebop:thead&gt;
* &lt;bebpp:cell&gt;...&lt;/cell&gt; ...
* &lt;/bebop:thead&gt;
* &lt;bebop:tbody&gt;
* &lt;bebop:trow&gt;
* &lt;bebpp:cell&gt;...&lt;/cell&gt; ...
* &lt;/bebop:trow&gt;
* ...
* &lt;/bebop:tbody&gt;
* &lt;/bebop:table&gt;
*
* @param s the page state
* @param p the parent {@link Element}
*/
@Override
public void generateXML(PageState s, Element p) {
TableModel model = getTableModel(s);
final boolean tableIsRegisteredWithPage =
s.getPage().stateContains(getControler());
if (model.nextRow()) {
Element table = p.newChildElement(BEBOP_TABLE, BEBOP_XML_NS);
exportAttributes(table);
generateExtraXMLAttributes(s, table);
if (getHeader() != null) {
getHeader().generateXML(s, table);
}
Element tbody = table.newChildElement(BEBOP_TABLEBODY, BEBOP_XML_NS);
if (m_striped) {
tbody.addAttribute("striped", "true");
}
final int modelSize = getColumnModel().size();
int row = 0;
logger.debug("Creating table rows...");
long start = System.currentTimeMillis();
do {
long rowStart = System.currentTimeMillis();
Element trow = tbody.newChildElement(BEBOP_TABLEROW,
BEBOP_XML_NS);
for (int i = 0; i < modelSize; i++) {
TableColumn tc = getColumn(i);
if (tc.isVisible(s)) {
TableCellRenderer r = tc.getCellRenderer();
if (r == null) {
r = m_defaultCellRenderer;
}
final int modelIndex = tc.getModelIndex();
Object key = model.getKeyAt(modelIndex);
Object value = model.getElementAt(modelIndex);
boolean selected =
isSelectedCell(s, key, new Integer(i));
if (tableIsRegisteredWithPage) {
/*StringBuffer coords = new StringBuffer(40);
coords.append(model.getKeyAt(modelIndex)).append(SEP).
append(i);
s.setControlEvent(getControler(), CELL_EVENT,
coords.toString());*/
s.setControlEvent(getControler(),
CELL_EVENT,
String.format("%s%s%d",
model.getKeyAt(
modelIndex),
SEP,
i));
}
Element cell = trow.newChildElement(BEBOP_CELL,
BEBOP_XML_NS);
tc.exportCellAttributes(cell);
long begin = System.currentTimeMillis();
r.getComponent(this, s, value, selected, key, row, i).
generateXML(s, cell);
logger.debug(String.format("until here i needed %d ms",
System.currentTimeMillis()
- begin));
}
}
row += 1;
logger.debug(
String.format("Created row in %d ms",
System.currentTimeMillis() - rowStart));
} while (model.nextRow());
logger.debug(String.format("Build table rows in %d ms",
System.currentTimeMillis() - start));
} else if (m_emptyView != null) {
m_emptyView.generateXML(s, p);
}
if (tableIsRegisteredWithPage) {
s.clearControlEvent();
}
}
protected Component getControler() {
return this;
}
/**
* Returns the table model in effect for the request represented by the
* page state.
*
* @param s represents the state of the page in the current request
* @return the table model used for outputting the table.
*/
public TableModel getTableModel(PageState s) {
return (TableModel) m_tableModel.get(s);
}
/**
* Initialize the request local <code>m_tableModel</code> field so that
* it is initialized with whatever model the table model builder returns
* for the request.
*/
private void initTableModel() {
m_tableModel = new RequestLocal() {
@Override
protected Object initialValue(PageState s) {
return m_modelBuilder.makeModel(Table.this, s);
}
};
}
/**
* Locks the table against further modifications. This also locks all
* the associated objects: the model builder, the column model, and the
* header components.
* @see com.arsdigita.util.Lockable#lock
*/
@Override
public void lock() {
getModelBuilder().lock();
getColumnModel().lock();
if (getHeader() != null) {
getHeader().lock();
}
super.lock();
}
/**
* An internal class that creates a table model around a set of data given
* as a <code>Object[][]</code>. The table models produced by this builder
* use row numbers, converted to strings, as the key for each column of a
* row.
*/
public static class MatrixTableModelBuilder
extends AbstractTableModelBuilder {
private final Object[][] m_data;
/**
* Constructor.
*
* @param data
*/
public MatrixTableModelBuilder(Object[][] data) {
m_data = data;
}
@Override
public TableModel makeModel(Table t, PageState s) {
return new TableModel() {
private int row = -1;
@Override
public int getColumnCount() {
return m_data[0].length;
}
@Override
public boolean nextRow() {
return (++row < m_data.length);
}
@Override
public Object getElementAt(int j) {
return m_data[row][j];
}
@Override
public Object getKeyAt(int j) {
return String.valueOf(row);
}
};
}
}
/**
* A {@link TableModel} that has no rows.
*/
public static final TableModel EMPTY_MODEL = new TableModel() {
@Override
public int getColumnCount() {
return 0;
}
@Override
public boolean nextRow() {
return false;
}
@Override
public Object getKeyAt(int column) {
throw new IllegalStateException("TableModel is empty");
}
@Override
public Object getElementAt(int column) {
throw new IllegalStateException("TableModel is empty");
}
};
}

View File

@ -0,0 +1,762 @@
/*
* Copyright (C) 2001-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.bebop;
import static com.arsdigita.bebop.Component.*;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.ChangeEvent;
import com.arsdigita.bebop.event.ChangeListener;
import com.arsdigita.bebop.event.EventListenerList;
import com.arsdigita.bebop.event.TreeExpansionEvent;
import com.arsdigita.bebop.event.TreeExpansionListener;
import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.tree.DefaultTreeCellRenderer;
import com.arsdigita.bebop.tree.TreeCellRenderer;
import com.arsdigita.bebop.tree.TreeModel;
import com.arsdigita.bebop.tree.TreeModelBuilder;
import com.arsdigita.bebop.tree.TreeNode;
import com.arsdigita.util.Assert;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.xml.Element;
import java.util.Iterator;
import org.apache.log4j.Logger;
/**
* Used to print a tree structure. Nodes can be in expanded or
* collapsed state. <code>Tree</code> uses the getChildren() and
* getRoot() methods from TreeModel and traverses the iterator to get
* to all the nodes.
*
* This class keeps track of which nodes are expanded and collapsed
* and the hierarchy of nodes, and displays the tree correspondingly.
*
* @author David Lutterkort
* @author Stanislav Freidin
* @author Tri Tran
* @version $Id$
*/
public class Tree extends SimpleComponent implements Resettable {
private static final Logger s_log =
Logger.getLogger(Tree.class);
private static final boolean s_selectAttributeEnabled =
Bebop.getConfig().treeSelectAttributeEnabled();
// Any node id in the currentState is equivalent
// to that node being expanded. If node id is
// NOT in the currentState, then it's collapsed.
private static final String CURRENT_STATE = "state";
private static final String EXPAND_EVENT = "expand";
private static final String COLLAPSE_EVENT = "collapse";
private static final String SELECT = "sel";
private static final String SELECT_EVENT = "s";
private static final boolean EXPANDED = true;
private static final boolean NOT_EXPANDED = false; // Collapsed
private static final boolean LEAF = true;
private static final boolean NOT_LEAF = false;
protected StringParameter m_currentState;
protected TreeModelBuilder m_builder;
private RequestLocal m_model;
private TreeModel m_tree;
private EventListenerList m_listeners;
private SingleSelectionModel m_selection;
private ChangeListener m_changeListener;
private Element treeElement;
private TreeCellRenderer m_renderer;
/**
* Constructs a new <code>Tree</code> using the specified
* {@link TreeModelBuilder}. The {@link TreeModelBuilder} will
* instantiate a {@link TreeModel} during each request.
*
* @param b the {@link TreeModelBuilder}
*/
public Tree(TreeModelBuilder b) {
super();
m_currentState = new StringParameter(CURRENT_STATE);
m_builder = b;
m_renderer = new DefaultTreeCellRenderer();
m_selection = new ParameterSingleSelectionModel(new StringParameter(SELECT));
m_listeners = new EventListenerList();
m_model = new RequestLocal() {
protected Object initialValue(PageState s) {
return getModelBuilder().makeModel(Tree.this, s);
}
};
m_tree = null;
}
/**
* Deprecated constructor that takes a default {@link TreeModel}
* and wraps it in a dummy TreeModelBuilder.
*
* @param t the TreeModel
* @deprecated This constructor has been deprecated in favor of
* <code>Tree(TreeModelBuilder b)</code>. It is not practical
* to hardwire the <code>TreeModel</code> into the <code>Tree</code>,
* since the model may change during each request. It is possible
* to write the model-instantiation code in
* {@link TreeModel#getRoot(PageState)}, but the
* {@link TreeModelBuilder} fits better into the pattern which has
* already been established by {@link List} and {@link Table}
*/
public Tree(TreeModel t) {
this(new WrapperModelBuilder());
m_tree = t;
}
/**
* Registers the two parameters to the page.
*/
public void register(Page p) {
Assert.isUnlocked(this);
p.addComponent(this);
p.addComponentStateParam(this, m_currentState);
p.addComponentStateParam(this, getSelectionModel().getStateParameter());
}
/**
* Clears the request state of the tree.
*/
public void reset(final PageState state) {
clearSelection(state);
clearExpansionState(state);
}
/**
* Returns the tree model used for this tree.
*
* @return a <code>TreeModel</code>.
* @see #setTreeModel setTreeModel
* @see TreeModel
* @deprecated Use {@link #getTreeModel(PageState)} instead
*/
public final TreeModel getTreeModel() {
return m_tree;
}
/**
* Returns the {@link TreeModel} used by the tree for the current
* request.
*
* @param s the page state
*/
public TreeModel getTreeModel(PageState s) {
return (TreeModel)m_model.get(s);
}
/**
* @return the {@link TreeModelBuilder} used to build the tree model
* for this tree.
*/
public final TreeModelBuilder getModelBuilder() {
return m_builder;
}
/**
* @param b the new {@link TreeModelBuilder} for the tree
*/
public void setModelBuilder(TreeModelBuilder b) {
Assert.isUnlocked(this);
m_builder = b;
}
/**
* Sets the tree model used for this tree.
*
* @return a <code>TreeModel</code>.
* @see #setTreeModel setTreeModel
* @see TreeModel
*/
public void setTreeModel(TreeModel m) {
Assert.isUnlocked(this);
m_tree = m;
}
/**
* Sets the selection model, which keeps track of which node is
* currently selected. It can be used to manipulate the selection
* programmatically.
*
* @param m the new selection model
*/
public void setSelectionModel(SingleSelectionModel m) {
Assert.isUnlocked(this);
m_selection = m;
s_log.debug("New model: " + m);
}
/**
* Gets the selection model, which keeps track of which node is
* currently selected. It can be used to manipulate the selection
* programmatically.
*
* @return the model used by the tree to keep track of the selected node.
*/
public final SingleSelectionModel getSelectionModel() {
return m_selection;
}
/**
* Gets the key for the selected node. This will only be a valid key
* if {@link #isSelected isSelected} is <code>true</code>.
*
* @param state represents the state of the current request
* @return the key for the selected node.
* @pre isSelected(state)
*/
public Object getSelectedKey(PageState state) {
return m_selection.getSelectedKey(state);
}
/**
* Sets the selection to the one with the specified key. If
* <code>key</code> was not selected already, fires the {@link
* ChangeEvent}.
*
* @param state represents the state of the current request
* @param key the key for the selected node
* @see #fireStateChanged fireStateChanged
*/
public void setSelectedKey(PageState state, Object key) {
m_selection.setSelectedKey(state, key);
}
/**
* Returns <code>true</code> if one of the nodes is currently selected.
*
* @param state represents the state of the current request
* @return <code>true</code> if one of the nodes is selected;
* <code>false</code> otherwise.
*/
public boolean isSelected(PageState state) {
return m_selection.isSelected(state);
}
/**
* Clears the selection in the request represented by <code>state</code>.
*
* @param state represents the state of the current request
* @post ! isSelected(state)
*/
public void clearSelection(PageState state) {
m_selection.clearSelection(state);
}
/**
* Tells whether the tree has state on the request for tree node
* expansion.
*/
public final boolean hasExpansionState(final PageState state) {
return state.getValue(m_currentState) != null;
}
/**
* Clears any tree node expansion state on the request.
*/
public final void clearExpansionState(final PageState state) {
state.setValue(m_currentState, null);
}
/**
* Creates the change listener used for forwarding change events fired by
* the selection model to change listeners registered with the tree. The
* returned change listener refires the event with the tree,
* rather than the selection model, as source.
*
* @return the change listener used internally by the tree.
*/
protected ChangeListener createChangeListener() {
return new ChangeListener() {
public void stateChanged(ChangeEvent e) {
fireStateChanged(e.getPageState());
}
};
}
/**
* Adds a change listener. A change event is fired whenever the selected
* tree node changes during the processing of a request. The change event
* that listeners receive names the tree as the source.
*
* @param l the change listener to run when the selected item changes in
* a request
* @pre ! isLocked()
*/
public void addChangeListener(ChangeListener l) {
Assert.isUnlocked(this);
if ( m_changeListener == null ) {
m_changeListener = createChangeListener();
if (s_log.isDebugEnabled()) {
s_log.debug("Adding listener " + l + " to " + this);
}
m_selection.addChangeListener(m_changeListener);
}
m_listeners.add(ChangeListener.class, l);
}
/**
* Removes a change listener. The listener should have been previously
* added with {@link #addChangeListener addChangeListener}, although no
* error is signalled if the change listener is not found among the
* tree's listeners.
*
* @param l the change listener to remove from the tree
*/
public void removeChangeListener(ChangeListener l) {
Assert.isUnlocked(this);
if (s_log.isDebugEnabled()) {
s_log.debug("Removing listener " + l + " from " + this);
}
m_listeners.remove(ChangeListener.class, l);
}
/**
* Fires a change event to signal that the selected list item has changed
* in the request represented by <code>state</code>. The source of the
* event is the tree.
*
* @param state represents the state of the current request
*/
protected void fireStateChanged(PageState state) {
Iterator
i=m_listeners.getListenerIterator(ChangeListener.class);
ChangeEvent e = null;
while (i.hasNext()) {
if ( e == null ) {
e = new ChangeEvent(this, state);
}
((ChangeListener) i.next()).stateChanged(e);
}
}
/**
* Adds a listener that is notified whenever a user clicks on any part
* of the tree, either to expand or collapse a node, or to select a
* node. The listener is run whenever {@link #respond respond} is
* called.
*
* @pre l != null
* @pre ! isLocked()
*/
public void addActionListener(ActionListener l) {
Assert.isUnlocked(this);
m_listeners.add(ActionListener.class, l);
}
/**
* Removes a previously added <code>ActionListener</code>.
* @see #addActionListener addActionListener
*/
public void removeActionListener(ActionListener l) {
Assert.isUnlocked(this);
m_listeners.remove(ActionListener.class, l);
}
/**
* Notifies listeners that some part of the tree was clicked by the
* user. The source of the event is the tree.
*
* @pre data != null
* @see #respond respond
*/
protected void fireActionEvent(PageState data) {
Iterator
i=m_listeners.getListenerIterator(ActionListener.class);
ActionEvent e = null;
while (i.hasNext()) {
if ( e == null ) {
e = new ActionEvent(this, data);
}
((ActionListener) i.next()).actionPerformed(e);
}
}
/**
* Adds a listener that is notified whenever a tree node is expanded or
* collpased, either by a user's click or by explicit calls to {@link
* #expand expand} or {@link #collapse collapse}.
*
* @pre l != null
* @pre ! isLocked()
*/
public void addTreeExpansionListener(TreeExpansionListener l) {
Assert.isUnlocked(this);
m_listeners.add(TreeExpansionListener.class, l);
}
/**
* Removes a previously added <code>TreeExpansionListener</code>.
*
* @pre ! isLocked()
* @see #addTreeExpansionListener addTreeExpansionListener
*/
public void removeTreeExpansionListener(TreeExpansionListener l) {
Assert.isUnlocked(this);
m_listeners.remove(TreeExpansionListener.class, l);
}
/**
* Notifies all registered {@link
* com.arsdigita.bebop.event.TreeExpansionListener
* TreeExpansionListeners} that a node in the tree has been expanded.
*
* @pre state != null
* @pre nodeKey != null
*/
protected void fireTreeExpanded(PageState state, Object nodeKey) {
Iterator i =
m_listeners.getListenerIterator(TreeExpansionListener.class);
TreeExpansionEvent e = null;
while (i.hasNext()) {
if ( e == null ) {
e = new TreeExpansionEvent(this, state, nodeKey);
}
((TreeExpansionListener) i.next()).treeExpanded(e);
}
}
/**
* Notifies all registered {@link
* com.arsdigita.bebop.event.TreeExpansionListener
* TreeExpansionListeners} that a node in the tree has been collapsed.
*
* @pre state != null
* @pre nodeKey != null
*/
protected void fireTreeCollapsed(PageState state, Object nodeKey) {
Iterator i =
m_listeners.getListenerIterator(TreeExpansionListener.class);
TreeExpansionEvent e = null;
while (i.hasNext()) {
if ( e == null ) {
e = new TreeExpansionEvent(this, state, nodeKey);
}
((TreeExpansionListener) i.next()).treeCollapsed(e);
}
}
/**
* Notifies the <code>Tree</code> that a node has been selected.
* Changes the currently selected tree component.
*/
public void respond(PageState data) throws javax.servlet.ServletException {
String action = data.getControlEventName();
String node = data.getControlEventValue();
if (EXPAND_EVENT.equals(action)) {
expand(node, data);
} else if (COLLAPSE_EVENT.equals(action)) {
collapse(node, data);
} else if ( SELECT_EVENT.equals(action) ) {
setSelectedKey(data, data.getControlEventValue());
} else {
throw new javax.servlet.ServletException("Unknown event '" + action + "'");
}
fireActionEvent(data);
}
//////////////////////////////
// MANAGE TREE'S NODE STATE //
//////////////////////////////
/**
* Determines whether the node at the specified display row is collapsed.
* @return <code>true</code> if the node at the specified display row is collapsed;
* <code>false</code> otherwise.
*/
public boolean isCollapsed(String nodeKey, PageState data) {
String stateString = (String) data.getValue(m_currentState);
String spaceId = " " + nodeKey + " ";
int idIndex;
if (stateString == null) {
return true;
} else {
idIndex = stateString.indexOf(spaceId);
}
// == -1 means it's not found in current state, thus it's collapsed
return (idIndex == -1);
}
/**
* Collapses a node in the tree and makes its children visible.
*
* @param nodeKey the key that the tree model uses to identify the
* node
* @param data represents the current request
*
* @pre nodeKey != null
* @pre data != null
*/
public void collapse(String nodeKey, PageState data) {
Assert.exists(nodeKey);
Assert.exists(data);
StringBuffer newCurrentState = new StringBuffer("");
String stateString = (String) data.getValue(m_currentState);
int idIndex;
String spaceId = " " + nodeKey + " ";
int idLength = spaceId.length();
if (stateString != null) {
idIndex = stateString.indexOf(spaceId);
// Found it; it should currently be expanded, so collapse it
if (idIndex != -1) {
newCurrentState
.append(stateString.substring(0,idIndex))
.append(" ");
if (stateString.length() > (idIndex + idLength)) {
newCurrentState.append(stateString.substring(idIndex+idLength));
}
data.setValue(m_currentState, newCurrentState.toString());
fireTreeCollapsed(data, nodeKey);
}
}
}
/**
* Expands a node in the tree and makes its children visible.
*
* @param nodeKey the key that the tree model uses to identify the
* node
* @param data represents the current request
*
* @pre nodeKey != null
* @pre data != null
*/
public void expand(String nodeKey, PageState data) {
Assert.exists(nodeKey);
Assert.exists(data);
String stateString = (String) data.getValue(m_currentState);
String spaceId = " " + nodeKey + " ";
StringBuffer newCurrentState = new StringBuffer("");
if (stateString != null) {
// Can't find it; it should currently be collapsed, so expand it
if ((stateString.indexOf(spaceId)) == -1) {
// Start with existing stateString...
newCurrentState.append(stateString);
// Add to newCurrentState string the new node Id
newCurrentState.append(spaceId);
// Set the value of the current state
data.setValue(m_currentState, newCurrentState.toString());
fireTreeExpanded(data, nodeKey);
}
} else {
// Add to newCurrentState string the new node Id
newCurrentState.append(spaceId);
// Set the value of the current state
data.setValue(m_currentState, newCurrentState.toString());
fireTreeExpanded(data, nodeKey);
}
}
/////////////////////////////////////////////
// PRINT THE TREE DIRECTLY OR GENERATE DOM //
/////////////////////////////////////////////
/**
* Returns the renderer currently used to render tree nodes.
*
* @return the current tree node renderer.
*/
public final TreeCellRenderer getCellRenderer() {
return m_renderer;
}
/**
* Sets the cell renderer to be used when generating output with
* {@link #generateXML generateXML}.
*
* @param r a <code>TreeCellRenderer</code> value
*/
public void setCellRenderer(TreeCellRenderer r) {
Assert.isUnlocked(this);
m_renderer = r;
}
private boolean hasSelectedChild(TreeModel tree, TreeNode node, PageState data, Object selKey) {
String nodeKey = (String) node.getKey();
if ( (selKey != null) && (selKey.equals(nodeKey) || selKey.toString().equals(nodeKey)) ) {
return true;
}
Iterator i = tree.getChildren(node, data);
while (i.hasNext()) {
TreeNode child = (TreeNode) i.next();
if (hasSelectedChild(tree, child, data, selKey)) {
// At this point we should close the opened DataQuery pointed to by Iterator (i).
// Since the data query is wrapped within DataQueryIterator, we don't have
// access to it directly, so this looks like the only viable option ...
while (i.hasNext()) { }
return true;
}
}
return false;
}
/**
* Builds a DOM representing the tree.
*
*/
protected void generateTree(PageState data, Element parent, TreeNode node,
TreeModel tree) {
Element t_node = parent.newChildElement ("bebop:t_node", BEBOP_XML_NS);
String nodeKey = (String) node.getKey();
Object selKey = getSelectedKey(data);
boolean isSelected = (selKey != null)
&& (selKey.equals(nodeKey) || selKey.toString().equals(nodeKey));
boolean hasChildren = tree.hasChildren(node, data);
if (s_selectAttributeEnabled) {
boolean hasSelectedChild = false;
if (!isSelected && hasChildren) {
hasSelectedChild = hasSelectedChild(tree, node, data, selKey);
}
t_node.addAttribute("isSelected", String.valueOf(isSelected | hasSelectedChild));
}
if (hasChildren) {
boolean collapsed = isCollapsed(nodeKey,data);
data.setControlEvent(this, collapsed ? EXPAND_EVENT : COLLAPSE_EVENT, nodeKey);
try {
t_node.addAttribute("href", data.stateAsURL());
} catch (java.io.IOException ioe) {
// TODO: stateAsURL failed
}
data.clearControlEvent();
if ( collapsed ) {
// Collapsed
t_node.addAttribute("collapsed", "t");
data.setControlEvent(this, SELECT_EVENT, nodeKey);
Component c = getCellRenderer().getComponent(this, data,
node.getElement(), isSelected, NOT_EXPANDED, NOT_LEAF,
nodeKey);
c.generateXML(data, t_node);
} else {
// Expanded
t_node.addAttribute("expanded", "t");
data.setControlEvent(this, SELECT_EVENT, nodeKey);
Component c = getCellRenderer().getComponent(this, data,
node.getElement(), isSelected, EXPANDED, NOT_LEAF,
nodeKey);
c.generateXML(data, t_node);
t_node.addAttribute("indentStart", "t");
for(Iterator i = tree.getChildren(node,data); i.hasNext(); ) {
generateTree(data, t_node, (TreeNode) i.next(), tree);
}
t_node.addAttribute("indentClose", "t");
}
} else {
// No children, no need for link...
t_node.addAttribute("childless", "t");
data.setControlEvent(this, SELECT_EVENT, nodeKey);
Component c = getCellRenderer().getComponent(this, data,
node.getElement(), isSelected, NOT_EXPANDED, LEAF, nodeKey);
c.generateXML(data, t_node);
}
}
/**
* Services the request by building a DOM tree with the nodes
* first and then the included page.
*/
public void generateXML(PageState data, Element parent) {
TreeModel tree = getTreeModel(data);
if ( ! isVisible(data) ) {
return;
}
treeElement = parent.newChildElement ("bebop:tree", BEBOP_XML_NS);
exportAttributes(treeElement);
TreeNode _rootNode = tree.getRoot(data);
generateTree(data, treeElement, _rootNode, tree);
}
/**
* Manage the selected item by manipulating the state parameter.
*
* @deprecated The {@link ParameterSingleSelectionModel} contains
* all the functionality of this class
*/
public static class TreeSingleSelectionModel
extends ParameterSingleSelectionModel {
public TreeSingleSelectionModel(ParameterModel m) {
super(m);
}
}
/**
* Locks the <code>Tree</code> and prohibits further modifications.
*/
public void lock() {
getModelBuilder().lock();
super.lock();
}
/**
* Returns the tree model of the tree. A wrapper class to make
* deprecated constructor work.
*/
private static class WrapperModelBuilder extends LockableImpl
implements TreeModelBuilder {
public WrapperModelBuilder() {
super();
}
public TreeModel makeModel(Tree t, PageState s) {
return t.getTreeModel();
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2001-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.bebop.table;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Table;
import com.arsdigita.util.LockableImpl;
/**
* A convenience for implementing <code>TableModelBuilder</code>s. This
* class provides a default implementation of the methods demanded by
* <code>Lockable</code>, so that implementors of
* <code>TableModelBuilder</code> only need to override the
* <code>makeModel</code> method.
*
* @author David Lutterkort
* @see TableModelBuilder
* @see com.arsdigita.util.Lockable
*
* @version $Id$
*/
public abstract class AbstractTableModelBuilder extends LockableImpl
implements TableModelBuilder {
/**
* Return a table model for the request represented by
* <code>s</code>. The table model contains all the data that is to be
* displayed in a table. The returned table model is used only during
* the duration of that request.
*
* @param t the table which will use this table model
* @param s represents the current request
* @return the data to be displayed in the table
*/
public abstract TableModel makeModel(Table t, PageState s);
}

View File

@ -0,0 +1,159 @@
/*
* Copyright (C) 2001-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.bebop.table;
import com.arsdigita.bebop.util.GlobalizationUtil ;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Table;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.util.Assert;
import com.arsdigita.util.LockableImpl;
/**
* The default renderer for table cells. This renderer is used by the
* {@link com.arsdigita.bebop.Table} component for rendering the table
* headers and cells if no other renderer is specified.
*
* <p> This renderer can operate in two different modes: <em>active</em>
* and <em>inactive</em> mode. In inactive mode, all objects are rendered
* by converting them to a string and enclosing that string in a {@link
* com.arsdigita.bebop.Label}. If the renderer is in active mode, this
* label is further enclosed in a control link. When the user clicks on
* this link, the table will fire an <code>TableActionEvent</code> whose
* <code>getKey()</code> and <code>getColumn()</code> method return the
* values of the <code>key</code> and <code>column</code> parameters that
* were passed into {@link #getComponent getComponent}.
*
* <p> In a nutshell, an active renderer will let the user click a link
* that causes a <code>TableActionEvent</code> for the corresponding cell,
* while an inactive renderer will display the values just as strings, thus
* making it impossible for the user to cause such an event.
*
* @author David Lutterkort
* @see com.arsdigita.bebop.Table
* @see com.arsdigita.bebop.event.TableActionEvent
*
* @version $Id$ */
public class DefaultTableCellRenderer extends LockableImpl
implements TableCellRenderer {
private boolean m_active;
private ThreadLocal m_label;
private ThreadLocal m_controlLink;
/**
* Creates a new table cell renderer. The table cell renderer is in
* inactive mode.
*/
public DefaultTableCellRenderer() {
this(false);
}
/**
* Creates a new table cell renderer. The <code>active</code> argument
* specifies whether the renderer should be active or not.
*
* @param active <code>true</code> if the renderer should generate links
* instead of just static labels.
*/
public DefaultTableCellRenderer(boolean active) {
m_active = active;
m_label = new ThreadLocal() {
protected Object initialValue() {
return new Label("");
}
};
m_controlLink = new ThreadLocal() {
protected Object initialValue() {
return new ControlLink((Label) m_label.get());
}
};
}
/**
* Return <code>true</code> if the renderer is in active mode. A
* rendererin active mode will enclose the objects it renders in links
* that, when clicked, will cause the containing table to fire a
* <code>TableActionEvent</code>.
*
* @return <code>true</code> if the renderer is in active mode.
*/
public final boolean isActive() {
return m_active;
}
/**
* Set the renderer to active or inactive mode.
*
* @param v <code>true</code> if the renderer should operate in active
* mode.
* @pre ! isLocked()
*/
public void setActive(boolean v) {
Assert.isUnlocked(this);
m_active = v;
}
/**
* Return the component that should be used to render the given
* <code>value</code>. Returns a {@link com.arsdigita.bebop.Label} if the
* renderer is active, and a {@link com.arsdigita.bebop.ControlLink} if
* the renderer is inactive.
*
* @pre table == null || table != null
*/
public Component getComponent(Table table, PageState state, Object value,
boolean isSelected, Object key,
int row, int column)
{
if ( ! isLocked() && table != null && table.isLocked() ) {
lock();
}
Label l;
if ( value instanceof com.arsdigita.bebop.Component ) {
return (com.arsdigita.bebop.Component) value;
} else if(value instanceof GlobalizedMessage) {
l = (Label) m_label.get();
l.setLabel((GlobalizedMessage) value);
} else {
l = (Label) m_label.get();
if ( value == null ) {
l.setLabel( (String) GlobalizationUtil.globalize("bebop.table.").localize());
l.setOutputEscaping(false);
} else {
l.setLabel(value.toString());
l.setOutputEscaping(true);
}
}
l.setFontWeight( (isSelected && m_active) ? Label.BOLD : null );
if (m_active && ! isSelected) {
return (ControlLink) m_controlLink.get();
} else {
return l;
}
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (C) 2001-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.bebop.table;
import java.util.ArrayList;
import java.util.Iterator;
import com.arsdigita.bebop.SingleSelectionModel;
import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.parameters.IntegerParameter;
import com.arsdigita.util.Assert;
/**
* Describe interface <code>TableColumnModel</code> here.
*
* @author David Lutterkort
* @version $Id$
*/
public class DefaultTableColumnModel implements TableColumnModel {
private static final String SELECTED_COLUMN="col";
private boolean m_locked;
private ArrayList m_columns;
private SingleSelectionModel m_selection;
public DefaultTableColumnModel() {
this(new Object[0]);
}
public DefaultTableColumnModel(SingleSelectionModel sel) {
this(new Object[0], sel);
}
public DefaultTableColumnModel(Object[] headers) {
this(headers,
new ParameterSingleSelectionModel(new IntegerParameter(SELECTED_COLUMN)) );
}
public DefaultTableColumnModel(Object[] headers, SingleSelectionModel sel) {
m_columns = new ArrayList();
m_selection = sel;
for (int i=0; i < headers.length; i++) {
add(new TableColumn(i, headers[i], new Integer(i)));
}
}
public void add(TableColumn column) {
Assert.isUnlocked(this);
m_columns.add(column);
}
public void add(int columnIndex, TableColumn column) {
Assert.isUnlocked(this);
m_columns.add(columnIndex, column);
}
public TableColumn get(int columnIndex) {
return (TableColumn) m_columns.get(columnIndex);
}
public void set(int columnIndex, TableColumn v) {
m_columns.set(columnIndex, v);
}
public int size() {
return m_columns.size();
}
public int getIndex(Object key) {
if ( key == null ) {
return -1;
}
for (int i=0; i<size(); i++) {
TableColumn t = get(i);
if ( key.equals(t.getHeaderKey()) ) {
return i;
}
}
return -1;
}
public Iterator columns() {
return m_columns.iterator();
}
public void remove(TableColumn column) {
Assert.isUnlocked(this);
m_columns.remove(column);
}
public final SingleSelectionModel getSelectionModel() {
return m_selection;
}
public void setSelectionModel(SingleSelectionModel model) {
Assert.isUnlocked(this);
m_selection = model;
}
public final void lock() {
m_locked = true;
}
public final boolean isLocked() {
return m_locked;
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (C) 2001-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.bebop.table;
import com.arsdigita.bebop.util.GlobalizationUtil ;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Table;
/**
* Render one cell in a table. The renderer returns a component whose
* {@link com.arsdigita.bebop.Component#generateXML generateXML} method
* will be called by the table to include the data for the cell in the
* table's output.
*
* <p> The table uses the returned component only until it calls the cell
* renderer again, so that cell renderers may reuse the same object in
* subsequent calls to {@link #getComponent getComponent}.
*
* <p> As an example, consider the following implementation of a table cell
* renderer, which simply converts the passed in <code>value</code> to a
* string and encloses it in a label. The cell renderer converts the passed
* in value to a string and uses that to set the text to display for a
* label. If the value is selected, the label is bolded. As an added twist,
* the table cell renderer uses only one label for each thread from which
* it is accessed (rather than creating a new <code>Label</code> for each
* call) by storing the label in a <code>ThreadLocal</code> variable.
*
* <pre>
* public class MyTableCellRenderer implements TableCellRenderer {
*
* private ThreadLocal m_label;
*
* public MyTableCellRenderer() {
* m_label = new ThreadLocal() {
* protected Object initialValue() {
* return new Label("");
* }
* };
* }
*
* public Component getComponent(Table table, PageState state, Object value,
* boolean isSelected, Object key,
* int row, int column) {
* Label l = (Label) m_label.get();
* l.setLabel(value.toString());
* l.setFontWeight( isSelected ? Label.BOLD : null );
* return l;
* }
* }
* </pre>
*
* @author David Lutterkort
* @see com.arsdigita.bebop.Table Table
* @version $Id$
*/
public interface TableCellRenderer {
/**
* Return a component with the visual representation for the passed in
* <code>key</code> and <code>value</code>.
*
* <p> The table sets the control event prior to calling this method, so
* that any control link returned as the component will, when clicked,
* cause the table to fire a <code>TableActionEvent</code> whose
* <code>getRowKey()</code> and <code>getColumn()</code> return the
* values of <code>key</code> and <code>column</code>. A simple cell
* renderer that achieves this would implement this method in the
* following way:
* <pre>
* public Component getComponent(Table table, PageState state, Object value,
* boolean isSelected, Object key,
* int row, int column) {
* return new ControlLink(value.toString());
* }
* </pre>
*
* <p> The <code>column</code> refers to a column in the table's {@link
* TableColumnModel}, i.e. the visual column on the screen, and not the
* table's representation of the underlying data in the {@link
* TableModel}.
*
* @param table the table requesting the rendering.
* @param state represents the state of the current request.
* @param value the data element to render as returned by the table
* model's {@link TableModel#getElementAt getElementAt(column)}.
* @param isSelected true if this item is selected.
* @param key the key identifying this row (and possibly column) as
* returned by the table model's {@link TableModel#getKeyAt
* getKeyAt(column)}
* @param row the number of the row in the table, the first row has
* number <code>0</code>.
* @param column the number of the table column.
* @return the component that should be used to render the
* <code>value</code>.
* @pre table != null
* @pre state != null
* @pre value != null
* @pre key != null
* @pre row >= 0
* @pre column >= 0 && column < table.getColumnModel().size()
* @post return != null
* @see TableColumnModel
*/
Component getComponent(Table table, PageState state, Object value,
boolean isSelected, Object key,
int row, int column);
}

View File

@ -0,0 +1,458 @@
/*
* Copyright (C) 2001-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.bebop.table;
import static com.arsdigita.bebop.Component.*;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SimpleComponent;
import com.arsdigita.util.Assert;
import com.arsdigita.bebop.util.Attributes;
import com.arsdigita.util.Lockable;
import com.arsdigita.xml.Element;
/**
* One column in a table. The <code>TableColumn</code> stores important
* display-related information about a table column, such as the column
* header, the renderers for the column header and ordinary cells in this
* column and from which column in the table model values should be taken
* when rendering table cells. The set of table columns for a table is
* maintained by a {@link TableColumnModel}.
*
* <p> <code>TableColumn</code> allows the column ordering to be different
* between the underlying {@link TableModel} and the view presented by the
* <code>Table</code>: each column contains a <code>modelIndex</code>
* property. This is the column that is retrieved from the
* <code>TableModel</code> when the values are displayed, regardless of the
* position of the <code>TableColumn</code> in the
* <code>TableColumnModel</code>. This makes it possible to display the
* same table model in several tables with reordered or omitted columns.
*
* <p> The <code>TableColumn</code> stores also the value and key used for
* the header of the column. These objects are passed to the header cell
* renderer when the header of the table is rendered. The value is usually
* used to generate the visible information for the table header, and is
* often a string. The key is usually used to identify the underlying
* object, or to just identify the column, and can be any object whose
* <code>toString()</code> method returns a representation that can be
* included in a URL. In the simplest case, this may just be an
* <code>Integer</code> containing the index of the column in the column
* model.
*
* @author David Lutterkort
* @see com.arsdigita.bebop.Table
* @see TableColumnModel
*
* @version $Id$ */
public class TableColumn extends SimpleComponent
implements Lockable {
/**
* The name of the width attribute used in the XML.
*/
private static final String WIDTH_ATTR = "width";
/**
* The name of the align attribute used in the XML.
*/
private static final String ALIGN_ATTR = "align";
/**
* The name of the valign attribute used in the XML.
*/
private static final String VALIGN_ATTR = "valign";
/**
* The number of the column in the table model from which to get values.
*/
private int m_modelIndex;
/**
* The renderer used for ordinary cells in this column. Null by default,
* which instructs the <code>Table</code> to use its default renderer.
*/
private TableCellRenderer m_cellRenderer;
/**
* The renderer used for the header of the column. Null by default, which
* instructs the <code>TableHeader</code> to use its default renderer.
*/
private TableCellRenderer m_headerRenderer;
/**
* The key for identifying the header. Will be passed to the header cell
* renderer.
*/
private Object m_headerKey;
/**
* The display value for identifying the header. Will be passed to the
* header cell renderer.
* Usually this will be a {@link Label} passed in by a pattern like
* {@code new Label(GlobalizedMessage)}. But it could be any object,
* e.g.an image as well. The use of a string is possible but strongly
* discouraged because it results in non-localizable UI.
*/
private Object m_headerValue;
/**
* The display attributes for each cell in this column
*/
private Attributes m_cellAttrs;
/**
* Creates a new table column with <code>modelIndex</code> 0 and header
* value and key equal to <code>null</code>.
*/
public TableColumn() {
this(0);
}
/**
* Creates a new table column with the given <code>modelIndex</code> and
* header value and key equal to <code>null</code>.
*
* @param modelIndex the index of the column in the table model from
* which to retrieve values
* @pre modelIndex >= 0
*/
public TableColumn(int modelIndex) {
this(modelIndex, null);
}
/**
* Creates a new table column with the given <code>modelIndex</code> and
* header value. The header key is equal to <code>null</code>.
*
* @param modelIndex the index of the column in the table model from
* which to retrieve values.
* @param value the value for the column header.
* @pre modelIndex >= 0
*/
public TableColumn(int modelIndex, Object value) {
this(modelIndex, value, null);
}
/**
* Creates a new table column with the given <code>modelIndex</code> and
* header value and key.
*
* @param modelIndex the index of the column in the table model from
* which to retrieve values.
* @param value the value for the column header.
* @param key the key for the column header.
* @pre modelIndex >= 0
*/
public TableColumn(int modelIndex, Object value, Object key) {
super();
m_modelIndex = modelIndex;
m_headerValue = value;
m_headerKey = key;
m_cellAttrs = new Attributes();
}
/**
* Return the renderer used for the column header. This is
* <code>null</code> by default, in which case the default renderer for
* the {@link TableHeader} of the table to which this column belongs is
* used.
*
* @return the renderer used for the column header.
*/
public final TableCellRenderer getHeaderRenderer() {
return m_headerRenderer;
}
/**
* Set the renderer used for the column header. The header key and value
* objects are passed to the renderer when the column header will be
* rendererd.
*
* @param v the new renderer for the column header.
* @see #getHeaderRenderer
* @see #getCellRenderer
*/
public void setHeaderRenderer(TableCellRenderer v) {
Assert.isUnlocked(this);
m_headerRenderer = v;
}
/**
* Return the renderer used for the cells in this column. This is
* <code>null</code> by default, in which case the default renderer of
* the {@link com.arsdigita.bebop.Table#getDefaultCellRenderer() table} to which this column
* belongs is used.
*
* @return the renderer used for the cells in this column.
*/
public final TableCellRenderer getCellRenderer() {
return m_cellRenderer;
}
/**
* Set the renderer used for cells in this column.
*
* @param v the new renderer for the cells in this column.
* @see #getCellRenderer
* @see #getHeaderRenderer
*/
public void setCellRenderer(TableCellRenderer v) {
Assert.isUnlocked(this);
m_cellRenderer = v;
}
/**
* Get the display value used for the header. This is the object that is
* passed to the renderer. Usually this will be a {@link Label} previously
* passed in by a pattern like {@code new Label(GlobalizedMessage)}.
* The use of a string is possible but strongly discouraged.
*
* @return the display value for the header.
*/
public final Object getHeaderValue() {
return m_headerValue;
}
/**
* Set the display value for the header. This object is passed through to
* the header renderer without any modifications.
* Usually this will be a {@link Label} passed in by a pattern like
* {@code new Label(GlobalizedMessage)}. The use of a string is possible
* but strongly discouraged because it results in non-localizable UI.
*
* @param value the new display value for the header.
* @see #getHeaderValue
*/
public void setHeaderValue(Object value) {
Assert.isUnlocked(this);
m_headerValue = value;
}
/**
* Get the key used to identify the header of this column. In the
* simplest case, this is an <code>Integer</code> containing the index of
* the column.
*
* @return the key used to identify the header of this column.
*/
public final Object getHeaderKey() {
return m_headerKey;
}
/**
* Set the key used to identify the header of this column.
*
* @param key the new key for identifying the header of this column.
* @see #getHeaderKey
*/
public void setHeaderKey(Object key) {
Assert.isUnlocked(this);
m_headerKey = key;
}
/**
* Get the index of the column from which values are taken in the {@link
* TableModel}.
*
* @return the index of the column in the table model from which values
* are taken.
* @see #setModelIndex setModelIndex
*/
public final int getModelIndex() {
return m_modelIndex;
}
/**
* Set the index of the column in the {@link TableModel} from which the
* values are taken when this column is rendered.
*
* @param v the new index of the column in the table model from which to
* take values.
*/
public void setModelIndex(int v) {
Assert.isUnlocked(this);
m_modelIndex = v;
}
/**
* Get the width for this column.
*
* @return the width of this column.
* @see #setWidth setWidth
*/
public String getWidth() {
return getAttribute(WIDTH_ATTR);
}
/**
* Set the width of this column. The string <code>v</code> is added as an
* attribute to the XML element for this column in the table header.
*
* @param v the width of this column
*/
public void setWidth(String v) {
Assert.isUnlocked(this);
setAttribute(WIDTH_ATTR, v);
}
/**
* Set the horizontal alignment this column. The string <code>v</code>
* is added as an attribute to the XML element for each cell in this column
*
* @param v the width of this column
*/
public void setAlign(String v) {
Assert.isUnlocked(this);
m_cellAttrs.setAttribute(ALIGN_ATTR, v);
}
/**
* Set the horizontal alignment this column's header. The string
* <code>v</code> is added as an attribute to the XML element for
* the column's header cell.
*
* @param v the width of this column */
public void setHeadAlign(String v) {
Assert.isUnlocked(this);
setAttribute(ALIGN_ATTR, v);
}
/**
* Set the vertical alignment this column. The string <code>v</code>
* is added as an attribute to the XML element for each cell in this column
*
* @param v the width of this column
*/
public void setVAlign(String v) {
Assert.isUnlocked(this);
m_cellAttrs.setAttribute(VALIGN_ATTR, v);
}
/**
* Set the vertical alignment this column's header. The string
* <code>v</code> is added as an attribute to the XML element for
* this column's header cell.
*
* @param v the width of this column */
public void setHeadVAlign(String v) {
Assert.isUnlocked(this);
setAttribute(VALIGN_ATTR, v);
}
/**
* Sets the style attribute for the column's
* cells. <code>style</code> should be a valid CSS style, since
* its value will be copied verbatim to the output and appear as a
* <tt>style</tt> attribute in the top level XML or HTML output
* element.
*
* @param style a valid CSS style description for use in the
* <tt>style</tt> attribute of an HTML tag
* @see <a href="#standard">Standard Attributes</a> */
public void setStyleAttr(String style) {
Assert.isUnlocked(this);
m_cellAttrs.setAttribute(STYLE, style);
}
/**
* Sets the style attribute for the column's header
* cell. <code>style</code> should be a valid CSS style, since its
* value will be copied verbatim to the output and appear as a
* <tt>style</tt> attribute in the top level XML or HTML output
* element.
*
* @param style a valid CSS style description for use in the
* <tt>style</tt> attribute of an HTML tag
* @see <a href="#standard">Standard Attributes</a> */
public void setHeadStyleAttr(String style) {
Assert.isUnlocked(this);
setAttribute(STYLE, style);
}
/**
* Sets the class attribute for the column's
* cells. <code>style</code> should be the name of a defined CSS
* class, since its value will be copied verbatim to the output
* and appear as a <tt>class</tt> attribute in the top level XML
* or HTML output element.
*
* @param style a valid CSS style description for use in the
* <tt>style</tt> attribute of an HTML tag
* @see <a href="#standard">Standard Attributes</a> */
public void setClassAttr(String c) {
Assert.isUnlocked(this);
m_cellAttrs.setAttribute(CLASS, c);
}
/**
* Sets the class attribute for the column's header
* cell. <code>style</code> should be the name of a defined CSS
* class, since its value will be copied verbatim to the output
* and appear as a <tt>class</tt> attribute in the top level XML
* or HTML output element.
*
* @param style a valid CSS style description for use in the
* <tt>style</tt> attribute of an HTML tag
* @see <a href="#standard">Standard Attributes</a> */
public void setHeadClassAttr(String c) {
Assert.isUnlocked(this);
setAttribute(CLASS, c);
}
/**
* Add all the XML attributes for this column.
*
* @param e the XML element to which attributes will be added.
*/
public void exportCellAttributes(Element e) {
m_cellAttrs.exportAttributes(e);
}
/**
* Add all the XML attributes for this column to this
* element. Package-friendly since it is only used by {@link
* TableHeader}.
*
* @param e the XML element to which attributes will be added.
*/
final void exportHeadAttributes(Element e) {
super.exportAttributes(e);
}
/**
* Throw an <code>UnsupportedOperationException</code>. This method can
* only be called if the table column is not properly contained in a
* table.
*
* @param s represents the current request
* @param e the parent element
*/
public void generateXML(PageState s, Element e) {
throw new UnsupportedOperationException("TableColumn used outside of a Table");
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2001-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.bebop.table;
import java.util.Iterator;
import com.arsdigita.bebop.SingleSelectionModel;
import com.arsdigita.util.Lockable;
/**
* Describe interface <code>TableColumnModel</code> here.
*
* @author David Lutterkort
* @version $Id$
*/
public interface TableColumnModel extends Lockable {
void add(TableColumn column);
TableColumn get(int columnIndex);
/**
* Insert a column at the given index. The columns from
* <code>columnIndex</code> on are shifted one up.
*
* @param columnIndex the index for the new column.
* @param column the table column to add to the model.
* @pre 0 <= columnIndex && columnIndex <= size()
*/
void add(int columnIndex, TableColumn column);
void set(int columnIndex, TableColumn v);
int size();
int getIndex(Object columnIdentifier);
Iterator columns();
void remove(TableColumn column);
SingleSelectionModel getSelectionModel();
void setSelectionModel(SingleSelectionModel model);
}

View File

@ -0,0 +1,298 @@
/*
* Copyright (C) 2001-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.bebop.table;
import static com.arsdigita.bebop.Component.*;
import java.util.Iterator;
import javax.servlet.ServletException;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SimpleComponent;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.event.EventListenerList;
import com.arsdigita.bebop.event.TableActionEvent;
import com.arsdigita.bebop.event.TableActionListener;
import com.arsdigita.util.Assert;
import com.arsdigita.xml.Element;
/**
* This class is used by {@link Table} in order to maintain its headers.
*
* <code>TableHeader</code> is responsible for setting the control event
* in order to notify the {@link Table} when one of the column headers
* is clicked.
*
* @author David Lutterkort
* @version $Id$
*/
public class TableHeader extends SimpleComponent {
/**
* The control event when the user clicks on a column header.
*/
public static final String HEAD_EVENT = "head";
private TableCellRenderer m_defaultRenderer;
private TableColumnModel m_columnModel;
private Table m_table;
private EventListenerList m_listeners;
/**
* Create a new <code>TableHeader</code>
*/
public TableHeader() {
this(new DefaultTableColumnModel());
}
/**
* Create a new <code>TableHeader</code>
*
* @param model the {@link TableColumnModel} that the header
* will use in order to generate and maintain the
* column headers.
*/
public TableHeader(TableColumnModel model) {
m_columnModel = model;
m_defaultRenderer = new DefaultTableCellRenderer();
m_listeners = new EventListenerList();
}
/**
* Add an {@link TableActionListener} to the header.
* The listener will be fired whenever this header is
* selected by the user.
*
* @param l the {@link TableActionListener} to add
*/
public void addTableActionListener(TableActionListener l) {
Assert.isUnlocked(this);
m_listeners.add(TableActionListener.class, l);
}
/**
* Remove a {@link TableActionListener} from the header
*
*@param l the {@link TableActionListener} to remove
*/
public void removeTableActionListener(TableActionListener l) {
Assert.isUnlocked(this);
m_listeners.remove(TableActionListener.class, l);
}
/**
* Notify all listeners that the header was selected
*
* @param state the page state
* @param rowKey the key of the selected row, as returned by
* <code>Table.getRowSelectionModel().getSelectedKey(state)</code>.
* this key may be null.
* @param column The index of the selected column
*
*/
protected void fireHeadSelected(PageState state,
Object rowKey, Integer column) {
Iterator
i=m_listeners.getListenerIterator(TableActionListener.class);
TableActionEvent e = null;
while (i.hasNext()) {
if ( e == null ) {
e = new TableActionEvent(this, state, rowKey, column);
}
((TableActionListener) i.next()).headSelected(e);
}
}
/**
* Respond to the current event by selecting the current
* column
*
* @param s the page state
*/
public void respond(PageState s) throws ServletException {
String event = s.getControlEventName();
if ( HEAD_EVENT.equals(event) ) {
String value = s.getControlEventValue();
// FIXME: ParameterData allows its value to be set to anything, even
// if it isn't compatible with the ParameterModel
// We need to change ParameterModel/Data to fail earlier on bad data
Integer col = new Integer(value);
getColumnModel().getSelectionModel().setSelectedKey(s, col);
fireHeadSelected(s, null, col);
} else {
throw new ServletException("Unknown event '" + event + "'");
}
}
/**
* @return the parent {@link Table}
*/
public final Table getTable() {
return m_table;
}
/**
* Set the parent {@link Table}
*
* @param v the parent table
*/
public void setTable(Table v) {
Assert.isUnlocked(this);
m_table = v;
}
/**
* @return the {@link TableColumnModel} which maintains the headers
*/
public final TableColumnModel getColumnModel() {
return m_columnModel;
}
/**
* Set the {@link TableColumnModel} which will maintain the headers
*
* @param v the new {@link TableColumnModel}
*/
public void setColumnModel(TableColumnModel v) {
Assert.isUnlocked(this);
m_columnModel = v;
}
/**
* @return the default {@link TableCellRenderer} for this header
*/
public final TableCellRenderer getDefaultRenderer() {
return m_defaultRenderer;
}
/**
* Set the default {@link TableCellRenderer} for this header.
* Header cells will be rendered with this renderer unless
* the column model specifies an alternative renderer.
*
* @param v the new default renderer
*/
public void setDefaultRenderer(TableCellRenderer v) {
Assert.isUnlocked(this);
m_defaultRenderer = v;
}
/**
* Generate the XML for this header. The XML will be of the form
* <blockquote><pre><code>
* &lt;bebop:thead&gt;
* &lt;bebop:cell&gt;...&lt;/bebop:cell&gt;
* ...
* &lt;/bebop:thead&gt;
* </code><pre></blockquote>
*
* @param s the page state
* @param p the parent element
*/
public void generateXML(PageState s, Element p) {
if ( isVisible(s) ) {
Element thead = p.newChildElement("bebop:thead", BEBOP_XML_NS);
exportAttributes(thead);
for (int i=0; i < m_columnModel.size(); i++) {
TableColumn t = m_columnModel.get(i);
if ( t.isVisible(s) ) {
TableCellRenderer r = t.getHeaderRenderer();
if ( r == null ) {
r = getDefaultRenderer();
}
boolean isSel = isSelected(s, t.getHeaderKey(), i);
Component c = r.getComponent(getTable(), s, t.getHeaderValue(), isSel,
t.getHeaderKey(), -1, i);
if (c != null) {
// supports having a table header disappear
// completely, mainly useful for the odd special case
// where a second-row element is being displayed.
Element cell = thead.newChildElement("bebop:cell", BEBOP_XML_NS);
t.exportHeadAttributes(cell);
// Mark the cell as selected if it is selected
if(isSel) {
cell.addAttribute("selected", "1");
}
// I added this check so that a table which is not
// added to the Page can still be used to render
// table XML.
boolean tableIsRegisteredWithPage =
s.getPage().stateContains(getControler());
if (tableIsRegisteredWithPage) {
s.setControlEvent(getControler(), HEAD_EVENT,
String.valueOf(i));
}
c.generateXML(s, cell);
if (tableIsRegisteredWithPage) {
s.clearControlEvent();
}
}
}
}
}
}
protected Component getControler() {
return this;
}
/**
* Determine whether the given column is selected. This information
* will be passed to the {@link TableCellRenderer} for this header.
*
* @param s the page state
* @param key the header key for the column as returned by
* <code>TableColumn.getHeaderKey()</code>
* @param column the index of the column to test
*/
protected boolean isSelected(PageState s, Object key, int column) {
if (getTable().getColumnSelectionModel() == null) {
return false;
}
Object sel = getTable()
.getColumnSelectionModel().getSelectedKey(s);
if(sel == null) {
return false;
}
return (column == ((Integer)sel).intValue());
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2001-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.bebop.table;
/**
* The <code>TableModel</code> is the abstraction a {@link
* com.arsdigita.bebop.Table Table} uses to access the data it
* displays. The table will ask its {@link TableModelBuilder} to
* instantiate a new table model once for each request it processes.
*
* <p> The table will request each element in the model at most once per
* request, moving through the rows with successive calls to {@link
* #nextRow}. For each row, the table retrieves the values and keys in each
* column with calls to {@link #getElementAt} and {@link #getKeyAt}.
*
* <p> The table data is accessed by the table by moving through the rows
* of the table model with calls to {@link #nextRow}. The data for each
* column in a row is represented by two objects: the data element which
* usually contains display information for that column and can be as
* simple as a string, and the key, which is used to identify the
* column. The key is usually a suitable representation of the primary key
* of the underlying object in the database. The key needs to be unique
* amongst all <em>rows</em> in the table model, but doesn't need to
* uniquely identify the row <em>and</em> column for that data item -
* all calls to {@link #getKeyAt} can return the same value for one row in
* the table model.
*
* @see com.arsdigita.bebop.Table Table
* @see TableModelBuilder
*
* @author David Lutterkort
* @version $Id$ */
public interface TableModel {
/**
* Return the number of columns this table model has.
*
* @return the number of columns in the table model
* @post return >= 0
*/
int getColumnCount();
/**
* Move to the next row and return true if the model is now positioned on
* a valid row. Initially, the table model is positioned before the first
* row. The table will call this method before it retrieves the data for
* the row with calls to {@link #getElementAt getElementAt} and {@link
* #getKeyAt getKeyAt}.
*
* <p> If this method returns <code>true</code>, subsequent calls to
* {@link #getElementAt getElementAt} and {@link #getKeyAt getKeyAt} have
* to succeed and return non-null objects. If this method returns
* <code>false</code>, the table assumes that it has traversed all the
* data contained in this model.
*
* @return <code>true</code> if the model is positioned on a valid row
*/
boolean nextRow();
/**
* Return the data element for the given column and the current row. The
* returned object will be passed to the table cell renderer as the
* <code>value</code> argument without modifications.
*
* @param columnIndex the number of the column for which to get data
* @return the object to pass to the table cell renderer for display
* @pre columnIndex >= 0 && columnIndex < getColumnCount()
* @post return != null
* @see TableCellRenderer
*/
Object getElementAt(int columnIndex);
/**
* Return the key for the given column and the current row. The key has
* to be unique for each <em>row</em> in the table model, but does not
* need to be unique for each row <em>and</em> column, though it may.
* The key is passed to the table cell renderer as the <code>key</code>
* argument.
*
* @param columnIndex the number of the column for which to get data
* @return the key for the given column and the current row.
* @pre columnIndex >= 0 && columnIndex < getColumnCount()
* @post return != null
* @see TableCellRenderer
*/
Object getKeyAt(int columnIndex);
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2001-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.bebop.table;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Table;
import com.arsdigita.util.Lockable;
/**
* Builds the request-specific table models. A table retrieves the data it
* displays by asking the table model builder for a table model. This is
* done for each request; the table does not cahce table models across
* requests. If such caching is desired, it has to be performed by the
* table model builder.
*
* <p> Typically, the table model builder will run a database query based
* on the information contained in the page state and return the result of
* the database query by wrapping it in a table model. The table will then
* traverse the table model during rendering.
*
* <p> The table model builder is automatically locked by the table to
* which it was added either through one of the {@link
* com.arsdigita.bebop.Table Table} constructors or with a call to {@link
* com.arsdigita.bebop.Table#setModelBuilder}.
*
* @see com.arsdigita.bebop.Table Table
* @see TableModel
*
* @author David Lutterkort
* @version $Id$
*/
public interface TableModelBuilder extends Lockable {
/**
* Return a table model for the request represented by
* <code>s</code>. The table model contains all the data that is to be
* displayed in a table. The returned table model is used only during
* the duration of that request.
*
* @param t the table which will use this table model
* @param s represents the current request
* @return the data to be displayed in the table
* @pre t != null
* @pre s != null
* @post return != null
*/
TableModel makeModel(Table t, PageState s);
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2001-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.bebop.tree;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Tree;
/**
* The interface
* describes how a tree node (component) can be rendered.
*
* @author David Lutterkort
* @author Tri Tran
*
* @see TreeModel
* @see TreeNode
* @see Tree
* @version $Id$ */
public class DefaultTreeCellRenderer implements TreeCellRenderer {
/**
* Returns node component to be displayed. The component's
* <code>generateXML</code> or <code>print</code> is called
* to render the node.
*
* @param tree the <code>Tree</code> in which this node is being displayed
* @param state represents the state of the current request
* @param value the object returned by the TreeModel for that node,
* such as pretty name
* @param isSelected true if the node is selected
* @param isExpanded true if the node is expanded (not collapsed)
* @param isLeaf true if the node is a leaf node (no children)
* @param key the object uniquely identify that node (primary key)
* @return the component used to generate the output for the node item
*/
public Component getComponent (Tree tree, PageState state, Object value,
boolean isSelected, boolean isExpanded,
boolean isLeaf, Object key) {
Label l = new Label(value.toString());
// Bold if selected
if (isSelected) {
l.setFontWeight(Label.BOLD);
return l;
}
// Currently, we are not doing anything special here for
// collapsed/expanded node, or leaf node... Also not doing anything
// fancy with node's key. We are leaving this to Tree.java for now
// to set the appropriate attributes...
return new ControlLink(l);
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2001-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.bebop.tree;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Tree;
/**
* The interface
* describes how a tree node (component) can be rendered.
*
* @author David Lutterkort
* @author Tri Tran
*
* @see TreeModel
* @see TreeNode
* @see Tree
* @version $Id$ */
public interface TreeCellRenderer {
/**
* Returns node component to be displayed. The component's
* <code>generateXML</code> or <code>print</code> is called
* to render the node.
*
* @param tree the <code>Tree</code> in which this node is being displayed
* @param state represents the state of the current request
* @param value the object returned by the TreeModel for that node
* @param isSelected true if the node is selected
* @param isExpanded true if the node is expanded (not collapsed)
* @param isLeaf true if the node is a leaf node (no children)
* @param key the object uniquely identify that node (primary key)
* @return the component used to generate the output for the node item
*/
Component getComponent (Tree tree, PageState state, Object value,
boolean isSelected, boolean isExpanded,
boolean isLeaf, Object key);
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2001-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.bebop.tree;
import com.arsdigita.bebop.PageState;
import java.util.Iterator;
/**
* The interface
* describes a model for a tree structure.
*
* @author David Lutterkort
* @author Stanislav Freidin
* @author Tri Tran
*
* @version $Id$ */
public interface TreeModel {
/**
* Obtain the root node of the tree, passing
* in PageState for permissioning purposes
*/
TreeNode getRoot(PageState data);
/**
* Check whether the node has children
*/
boolean hasChildren(TreeNode n, PageState data);
/**
* Check whether a given node has children, passing
* in PageState for permissioning purposes
*/
Iterator getChildren(TreeNode n, PageState data);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2001-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.bebop.tree;
import com.arsdigita.bebop.Tree;
import com.arsdigita.bebop.PageState;
import com.arsdigita.util.Lockable;
/**
* The interface builds a
* {@link TreeModel} for a {@link Tree}.
*
* @author Stanislav Freidin
*
* @version $Id$ */
public interface TreeModelBuilder extends Lockable {
/**
* Builds a {@link TreeModel} to be used in the current request
*
* @param t The {@link Tree} that will use the model
* @param s The page state
*/
TreeModel makeModel(Tree t, PageState s);
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2001-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.bebop.tree;
/**
* The interface
* describes a node for a tree.
*
* @author David Lutterkort
* @author Stanislav Freidin
* @author Tri Tran
*
* @version $Id$ */
public interface TreeNode {
/**
* Obtain a unique ID representing the node
*/
Object getKey();
/**
* Get the element of the tree node. The concrete type
* of the object returned is specific to each implementation
* of the <code>TreeModel</code> and should be documented
* there.
*
* @return the element for the tree node
*/
Object getElement();
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2001-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.dispatcher;
import javax.servlet.http.HttpServletRequest;
/**
* <tt>AccessDeniedException</tt> is the runtime exception that is thrown
* whenever the current user does not have access to the requested resources.
*
* @author Michael Pih
* @version $Id$
*/
public class AccessDeniedException extends RuntimeException {
public final static String ACCESS_DENIED =
"com.arsdigita.cms.dispatcher.AccessDeniedException";
// The default error detail message.
private final static String ERROR_MSG = "Access Denied";
// The URL where the AccessDeniedException is thrown.
private String m_url;
/**
* Constructs an AccessDeniedException with the default detail message.
*/
public AccessDeniedException() {
this(ERROR_MSG);
}
/**
* Constructs an AccessDeniedException with the specified detail message.
*
* @param msg The error detail message
*/
public AccessDeniedException(String msg) {
super(msg);
// Try and fetch the current request URL.
HttpServletRequest request = DispatcherHelper.getRequest();
if ( request != null ) {
m_url = DispatcherHelper.getRequest().getRequestURI();
request.setAttribute(ACCESS_DENIED, m_url);
} else {
m_url = null;
}
}
/**
* Fetches the URL where the AccessDeniedException originated.
*
* @return The original URL
*/
public String getOriginalURL() {
return m_url;
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2003-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.toolbox.ui;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Resettable;
import com.arsdigita.bebop.SimpleComponent;
import com.arsdigita.util.Assert;
import com.arsdigita.util.SequentialMap;
import com.arsdigita.xml.Element;
import java.util.Iterator;
import org.apache.log4j.Logger;
/**
*
* @version $Id$
*/
public abstract class ComponentMap extends SimpleComponent
implements Resettable {
private static final Logger s_log = Logger.getLogger(ComponentMap.class);
private final SequentialMap m_components;
public ComponentMap() {
m_components = new SequentialMap();
}
public final Iterator children() {
return m_components.values().iterator();
}
public void reset(final PageState state) {
s_log.debug("Resetting my children");
final Iterator iter = children();
while (iter.hasNext()) {
final Object component = iter.next();
if (component instanceof Resettable) {
((Resettable) component).reset(state);
}
}
}
public final void put(final Object key, final Component component) {
Assert.isUnlocked(this);
Assert.exists(key, Object.class);
m_components.put(key, component);
}
public final Component get(final Object key) {
return (Component) m_components.get(key);
}
public final boolean containsKey(final Object key) {
return m_components.containsKey(key);
}
public final boolean containsValue(final Component component) {
return m_components.containsValue(component);
}
public abstract void generateXML(final PageState state,
final Element parent);
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2003-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.toolbox.ui;
import static com.arsdigita.bebop.Component.*;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.PageState;
import com.arsdigita.xml.Element;
import org.apache.log4j.Logger;
/**
* <p>A simple layout panel with top, bottom, left, right, and body
* sections.</p>
*
* @author Justin Ross &lt;jross@redhat.com&gt;
* @version $Id$
*/
public class LayoutPanel extends ComponentMap {
private static final Logger s_log = Logger.getLogger(LayoutPanel.class);
public final void setTop(final Component top) {
put("top", top);
}
public final void setLeft(final Component left) {
put("left", left);
}
public final void setBody(final Component body) {
put("body", body);
}
public final void setRight(final Component right) {
put("right", right);
}
public final void setBottom(final Component bottom) {
put("bottom", bottom);
}
@Override
public void generateXML(final PageState state, final Element parent) {
if (isVisible(state)) {
final Element layout = parent.newChildElement
("bebop:layoutPanel", BEBOP_XML_NS);
section(state, layout, "top");
section(state, layout, "left");
section(state, layout, "body");
section(state, layout, "right");
section(state, layout, "bottom");
}
}
private void section(final PageState state,
final Element parent,
final String key) {
final Component section = get(key);
if (section != null) {
final Element elem = parent.newChildElement
("bebop:" + key, BEBOP_XML_NS);
section.generateXML(state, elem);
}
}
}

View File

@ -0,0 +1,405 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.parameters.BigDecimalParameter;
import com.arsdigita.globalization.GlobalizedMessage;
/**
* Centralize place for all constants used in the admin UI.
*
* @author David Dao
* @version $Revision$ $Date$
*/
interface AdminConstants {
/**
* The XML namespace used by admin components.
*/
String ADMIN_XML_NS = "http://www.arsdigita.com/admin-ui/1.0";
/**
* Globalization resource for admin ui.
*/
String BUNDLE_NAME = "com.arsdigita.ui.admin.AdminResources";
/**
* Navigational dimension bar labels.
*/
// Label MY_WORKSPACE_LABEL = new Label
// (new GlobalizedMessage("ui.admin.nav.workspace",
// BUNDLE_NAME));
// Label LOG_OUT_LABEL = new Label
// (new GlobalizedMessage("ui.admin.nav.logout",
// BUNDLE_NAME));
/**
* Administration page title
*/
Label PAGE_TITLE_LABEL = new Label(new GlobalizedMessage("ui.admin.dispatcher.title",
BUNDLE_NAME));
/**
* Administration main tab names.
*/
Label USER_TAB_TITLE = new Label(new GlobalizedMessage("ui.admin.tab.user",
BUNDLE_NAME));
Label GROUP_TAB_TITLE = new Label(new GlobalizedMessage("ui.admin.tab.group",
BUNDLE_NAME));
Label APPLICATIONS_TAB_TITLE = new Label(new GlobalizedMessage("ui.admin.tab.applications",
BUNDLE_NAME));
Label SYSINFO_TAB_TITLE = new Label(new GlobalizedMessage("ui.admin.tab.sysinfo.title", BUNDLE_NAME));
GlobalizedMessage USER_NAVBAR_TITLE = new GlobalizedMessage("ui.admin.tab.user.navbartitle",
BUNDLE_NAME);
/**
* Tabbed pane indices
*/
int USER_TAB_INDEX = 2;
int GROUP_TAB_INDEX = 3;
/**
* User tab name
*/
Label USER_TAB_SUMMARY = new Label(new GlobalizedMessage("ui.admin.tab.user.summary",
BUNDLE_NAME));
Label USER_TAB_BROWSE = new Label(new GlobalizedMessage("ui.admin.tab.user.browse",
BUNDLE_NAME));
Label USER_TAB_SEARCH = new Label(new GlobalizedMessage("ui.admin.tab.user.search",
BUNDLE_NAME));
Label USER_TAB_CREATE_USER = new Label(new GlobalizedMessage("ui.admin.tab.user.createuser",
BUNDLE_NAME));
int USER_TAB_SUMMARY_INDEX = 0;
int USER_TAB_BROWSE_INDEX = 1;
int USER_TAB_SEARCH_INDEX = 2;
int USER_TAB_CREATE_USER_INDEX = 3;
/**
* Global state parameters.
*/
BigDecimalParameter GROUP_ID_PARAM = new BigDecimalParameter("group_id");
BigDecimalParameter APPLICATIONS_ID_PARAM = new BigDecimalParameter("application_id");
BigDecimalParameter USER_ID_PARAM = new BigDecimalParameter("user_id");
/**
* User summary panel.
*/
Label SUMMARY_PANEL_HEADER = new Label(
new GlobalizedMessage("ui.admin.user.summarypanel.header", BUNDLE_NAME));
Label CREATE_USER_LABEL = new Label(new GlobalizedMessage(
"ui.admin.user.summarypanel.createUser", BUNDLE_NAME));
Label TOTAL_USERS_LABEL = new Label(new GlobalizedMessage(
"ui.admin.user.summarypanel.totalusers", BUNDLE_NAME));
/**
* User browse panel.
*/
Label BROWSE_USER_PANEL_HEADER = new Label(new GlobalizedMessage(
"ui.admin.user.browsepanel.header",
BUNDLE_NAME));
Label USER_INFO_LABEL = new Label(new GlobalizedMessage("ui.admin.user.userinfo.header",
BUNDLE_NAME));
Label USER_EDIT_PANEL_HEADER = new Label(new GlobalizedMessage("ui.admin.user.useredit.header",
BUNDLE_NAME));
Label USER_GROUP_PANEL_HEADER = new Label(new GlobalizedMessage(
"ui.admin.user.groupmembership.header",
BUNDLE_NAME));
Label USER_DELETE_FAILED_PANEL_HEADER = new Label(new GlobalizedMessage(
"ui.admin.user.action.delete.failed.header",
BUNDLE_NAME));
Label USER_PASSWORD_PANEL_HEADER = new Label(new GlobalizedMessage(
"ui.admin.user.password.header",
BUNDLE_NAME));
Label USER_ACTION_PANEL_HEADER = new Label(new GlobalizedMessage("ui.admin.user.action.header",
BUNDLE_NAME));
Label USER_ACTION_CONTINUE = new Label(new GlobalizedMessage("ui.admin.user.action.continue",
BUNDLE_NAME));
Label USER_DELETE_LABEL = new Label(new GlobalizedMessage("ui.admin.user.delete.label",
BUNDLE_NAME));
Label USER_BAN_LABEL = new Label(new GlobalizedMessage("ui.admin.user.ban.label",
BUNDLE_NAME));
Label USER_UNBAN_LABEL = new Label(new GlobalizedMessage("ui.admin.user.unban.label",
BUNDLE_NAME));
GlobalizedMessage USER_DELETE_CONFIRMATION = new GlobalizedMessage(
"ui.admin.user.delete.confirm", BUNDLE_NAME);
GlobalizedMessage USER_BAN_CONFIRMATION = new GlobalizedMessage("ui.admin.user.ban.confirm",
BUNDLE_NAME);
GlobalizedMessage USER_UNBAN_CONFIRMATION = new GlobalizedMessage("ui.admin.user.unban.confirm",
BUNDLE_NAME);
GlobalizedMessage USER_DELETE_FAILED_MSG = new GlobalizedMessage(
"ui.admin.user.delete.failed.label", BUNDLE_NAME);
Label USER_TAB_EXTREME_ACTION_LABEL = new Label(new GlobalizedMessage(
"ui.admin.user.browsepanel.extremeaction",
BUNDLE_NAME));
Label UPDATE_USER_PASSWORD_LABEL = new Label(new GlobalizedMessage(
"ui.admin.user.browsepanel.updatePassword",
BUNDLE_NAME));
Label BECOME_USER_LABEL = new Label(
new GlobalizedMessage("ui.admin.user.browsepanel.becomeUser",
BUNDLE_NAME));
/**
* Create new user panel.
*/
Label CREATE_USER_PANEL_HEADER = new Label(new GlobalizedMessage(
"ui.admin.user.createpanel.header",
BUNDLE_NAME));
/**
* User search panel.
*/
Label SEARCH_PANEL_HEADER = new Label(new GlobalizedMessage("ui.admin.user.search.header",
BUNDLE_NAME));
Label PASSWORD_FORM_LABEL_PASSWORD = new Label(new GlobalizedMessage(
"ui.admin.user.userpasswordform.passwordlabel",
BUNDLE_NAME));
Label PASSWORD_FORM_LABEL_CONFIRMATION_PASSWORD = new Label(new GlobalizedMessage(
"ui.admin.user.userpasswordform.confirmpasswordlabel",
BUNDLE_NAME));
Label PASSWORD_FORM_LABEL_QUESTION = new Label(new GlobalizedMessage(
"ui.admin.user.userpasswordform.question",
BUNDLE_NAME), false);
Label PASSWORD_FORM_LABEL_ANSWER = new Label(new GlobalizedMessage(
"ui.admin.user.userpasswordform.answer",
BUNDLE_NAME), false);
GlobalizedMessage PASSWORD_FORM_SUBMIT = new GlobalizedMessage(
"ui.admin.user.userpasswordform.submit",
BUNDLE_NAME);
/**
* Constants for user add/edit form.
*/
String USER_FORM_ADD = "user-add-form";
String USER_FORM_EDIT = "user-edit-form";
String USER_FORM_INPUT_FIRST_NAME = "firstname";
String USER_FORM_INPUT_LAST_NAME = "lastname";
String USER_FORM_INPUT_PASSWORD = "password";
String USER_FORM_INPUT_PASSWORD_CONFIRMATION = "password_confirmation";
String USER_FORM_INPUT_QUESTION = "question";
String USER_FORM_INPUT_ANSWER = "answer";
String USER_FORM_INPUT_PRIMARY_EMAIL = "email";
String USER_FORM_INPUT_ADDITIONAL_EMAIL = "additional_email";
String USER_FORM_INPUT_SCREEN_NAME = "screenname";
String USER_FORM_INPUT_SSO = "sso_login";
String USER_FORM_INPUT_URL = "url";
String USER_FORM_INPUT_URL_DEFAULT = "http://";
Label USER_FORM_LABEL_FIRST_NAME = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.firstname",
BUNDLE_NAME));
Label USER_FORM_LABEL_LAST_NAME = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.lastname",
BUNDLE_NAME));
Label USER_FORM_LABEL_PASSWORD = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.password",
BUNDLE_NAME));
Label USER_FORM_LABEL_PASSWORD_CONFIRMATION = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.confirmation",
BUNDLE_NAME));
Label USER_FORM_LABEL_QUESTION = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.question",
BUNDLE_NAME));
Label USER_FORM_LABEL_ANSWER = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.answer",
BUNDLE_NAME));
Label USER_FORM_LABEL_PRIMARY_EMAIL = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.primaryemail",
BUNDLE_NAME));
Label USER_FORM_LABEL_ADDITIONAL_EMAIL = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.additionalemail",
BUNDLE_NAME));
Label USER_FORM_LABEL_ADDITIONAL_EMAIL_LIST = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.additionalemaillist",
BUNDLE_NAME));
Label USER_FORM_LABEL_SCREEN_NAME = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.screenname",
BUNDLE_NAME));
Label USER_FORM_LABEL_SSO = new Label(new GlobalizedMessage(
"ui.admin.user.addeditform.ssologinname",
BUNDLE_NAME));
Label USER_FORM_LABEL_URL = new Label(new GlobalizedMessage("ui.admin.user.addeditform.url",
BUNDLE_NAME));
Label USER_FORM_DELETE_ADDITIONAL_EMAIL = new Label(
new GlobalizedMessage("ui.admin.user.addeditform.deleteemail",
BUNDLE_NAME));
GlobalizedMessage USER_FORM_SUBMIT = new GlobalizedMessage("ui.admin.user.addeditform.submit",
BUNDLE_NAME);
GlobalizedMessage USER_FORM_ERROR_SCREEN_NAME_NOT_UNIQUE = new GlobalizedMessage(
"ui.admin.user.addeditform.error.screenname.notunique",
BUNDLE_NAME);
GlobalizedMessage USER_FORM_ERROR_PRIMARY_EMAIL_NOT_UNIQUE = new GlobalizedMessage(
"ui.admin.user.addeditform.error.primaryemail.notunique",
BUNDLE_NAME);
GlobalizedMessage USER_FORM_ERROR_PASSWORD_NOT_MATCH = new GlobalizedMessage(
"ui.admin.user.addeditform.error.password.notmatch",
BUNDLE_NAME);
GlobalizedMessage USER_FORM_ERROR_ANSWER_NULL = new GlobalizedMessage(
"ui.admin.user.addeditform.error.answer.null",
BUNDLE_NAME);
GlobalizedMessage USER_FORM_ERROR_ANSWER_WHITESPACE = new GlobalizedMessage(
"ui.admin.user.addeditform.error.answer.whitespace",
BUNDLE_NAME);
/**
* Constants for group add/edit form.
*/
String GROUP_FORM_ADD = "group-add-form";
String GROUP_FORM_EDIT = "group-edit-form";
String GROUP_FORM_INPUT_NAME = "name";
String GROUP_FORM_INPUT_PRIMARY_EMAIL = "email";
Label GROUP_FORM_LABEL_NAME = new Label(new GlobalizedMessage(
"ui.admin.groups.addeditform.namelabel",
BUNDLE_NAME));
Label GROUP_FORM_LABEL_PRIMARY_EMAIL = new Label(new GlobalizedMessage(
"ui.admin.groups.addeditform.primaryemaillabel",
BUNDLE_NAME));
GlobalizedMessage GROUP_FORM_SUBMIT
= new GlobalizedMessage("ui.admin.groups.addeditform.submit", BUNDLE_NAME);
/**
* Constants for group administration tab.
*/
Label GROUP_ACTION_CONTINUE = new Label(new GlobalizedMessage("ui.admin.groups.actioncontinue",
BUNDLE_NAME));
GlobalizedMessage GROUP_DELETE_FAILED_MSG = new GlobalizedMessage(
"ui.admin.groups.groupdeletefailed",
BUNDLE_NAME);
Label GROUP_INFORMATION_HEADER = new Label(new GlobalizedMessage(
"ui.admin.groups.groupinformation",
BUNDLE_NAME));
Label SUBGROUP_HEADER = new Label(new GlobalizedMessage("ui.admin.groups.subgroups",
BUNDLE_NAME));
Label GROUP_EDIT_HEADER = new Label(new GlobalizedMessage("ui.admin.groups.groupedit",
BUNDLE_NAME));
Label ADD_SUBGROUP_LABEL = new Label(new GlobalizedMessage("ui.admin.groups.add",
BUNDLE_NAME));
Label SUBMEMBER_HEADER = new Label(new GlobalizedMessage("ui.admin.groups.submembers",
BUNDLE_NAME));
Label DELETE_GROUP_LABEL = new Label(new GlobalizedMessage("ui.admin.groups.delete",
BUNDLE_NAME));
Label GROUP_EXTREME_ACTIONS_HEADER = new Label(new GlobalizedMessage(
"ui.admin.groups.extremeaction",
BUNDLE_NAME));
Label GROUP_DELETE_FAILED_HEADER = new Label(new GlobalizedMessage(
"ui.admin.groups.deletefailed",
BUNDLE_NAME));
Label ADD_GROUP_LABEL = new Label(new GlobalizedMessage("ui.admin.groups.addgrouplabel",
BUNDLE_NAME));
Label EDIT_GROUP_LABEL = new Label(new GlobalizedMessage("ui.admin.groups.edit",
BUNDLE_NAME));
Label SUBGROUP_COUNT_LABEL = new Label(new GlobalizedMessage(
"ui.admin.groups.subgroupcountlabel",
BUNDLE_NAME));
String GROUP_DELETE_CONFIRMATION = "Are you sure you want to delete this group?";
Label ADD_SUBMEMBER_LABEL = new Label(new GlobalizedMessage("ui.admin.groups.addsubmemberlabel",
BUNDLE_NAME));
Label REMOVE_SUBMEMBER_LABEL = new Label(new GlobalizedMessage(
"ui.admin.groups.removesubmemberlabel",
BUNDLE_NAME));
Label ADD_EXISTING_GROUP_TO_SUBGROUPS_LABEL = new Label(new GlobalizedMessage(
"ui.admin.groups.addExisting",
BUNDLE_NAME));
Label REMOVE_SUBGROUP_LABEL = new Label(new GlobalizedMessage("ui.admin.groups.removeExisting",
BUNDLE_NAME));
Label GROUP_SEARCH_LABEL = new Label(
new GlobalizedMessage("ui.admin.groups.search", BUNDLE_NAME));
GlobalizedMessage SEARCH_BUTTON = new GlobalizedMessage("ui.admin.groups.button.search",
BUNDLE_NAME);
Label GROUP_NO_RESULTS = new Label(new GlobalizedMessage("ui.admin.groups.searchForm.noResults",
BUNDLE_NAME));
Label FOUND_GROUPS_TITLE = new Label(new GlobalizedMessage("ui.admin.groups.found.title",
BUNDLE_NAME));
Label PICK_GROUPS = new Label(new GlobalizedMessage("ui.admin.groups.select.explanation",
BUNDLE_NAME));
GlobalizedMessage SAVE_BUTTON = new GlobalizedMessage("ui.admin.save", BUNDLE_NAME);
String SEARCH_QUERY = "query";
}

View File

@ -0,0 +1,241 @@
/*
* Copyright (C) 2012 Peter Boy <pb@zes.uni-bremen.de> 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.ui.admin;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageFactory;
import com.arsdigita.bebop.TabbedPane;
import com.arsdigita.dispatcher.AccessDeniedException;
import com.arsdigita.dispatcher.DispatcherHelper;
import com.arsdigita.templating.Templating;
import com.arsdigita.util.Assert;
import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.web.BaseApplicationServlet;
import com.arsdigita.web.LoginSignal;
import com.arsdigita.xml.Document;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.CcmSessionContext;
import org.libreccm.core.PermissionManager;
import org.libreccm.core.Privilege;
import org.libreccm.core.PrivilegeRepository;
import org.libreccm.core.Subject;
import org.libreccm.web.CcmApplication;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Web Developer Support Application Servlet class, central entry point to
* create and process the applications UI.
*
* We should have subclassed BebopApplicationServlet but couldn't overwrite
* doService() method to add permission checking. So we use our own page
* mapping. The general logic is the same as for BebopApplicationServlet.
*
* {
*
* @see com.arsdigita.bebop.page.BebopApplicationServlet}
*
* @author Jens Pelzetter
* @author pb
*/
public class AdminServlet extends BaseApplicationServlet implements
AdminConstants {
private static final long serialVersionUID = -3912367600768871630L;
/**
* Logger instance for debugging
*/
//private static final Logger LOGGER = Logger.getLogger(AdminServlet.class.getName());
/**
* URL (pathinfo) -> Page object mapping. Based on it (and the http request
* url) the doService method to selects a page to display
*/
private final Map<String, Page> pages = new HashMap<String, Page>();
/**
* User extension point, overwrite this method to setup a URL - page mapping
*
* @throws ServletException
*/
@Override
public void doInit() throws ServletException {
addPage("/", buildAdminIndexPage()); // index page at address ~/ds
// addPage("/index.jsp", buildIndexPage()); // index page at address ~/ds
}
/**
* Central service method, checks for required permission, determines the
* requested page and passes the page object to PresentationManager.
*
* @param sreq
* @param sresp
* @param app
*
* @throws ServletException
* @throws IOException
*/
@Override
public final void doService(final HttpServletRequest sreq,
final HttpServletResponse sresp,
final CcmApplication app) throws
ServletException, IOException {
// /////// Some preparational steps ///////////////
/* Determine access privilege: only logged in users may access DS */
final CdiUtil cdiUtil = new CdiUtil();
final CcmSessionContext sessionContext;
try {
sessionContext = cdiUtil.findBean(
CcmSessionContext.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup session context", ex);
}
final Subject subject = sessionContext.getCurrentSubject();
if (subject == null) {
throw new LoginSignal(sreq);
}
final PrivilegeRepository privilegeRepository;
try {
privilegeRepository = cdiUtil.findBean(PrivilegeRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup PrivilegeRepository", ex);
}
final Privilege adminPrivilege = privilegeRepository.retrievePrivilege(
"admin");
final PermissionManager permissionManager;
try {
permissionManager = cdiUtil.findBean(PermissionManager.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to look up PermissionManager", ex);
}
if (!permissionManager.isPermitted(adminPrivilege, null, subject)) {
throw new AccessDeniedException("User is not an administrator");
}
/* Want admin to always show the latest stuff... */
DispatcherHelper.cacheDisable(sresp);
// /////// Everything OK here - DO IT ///////////////
String pathInfo = sreq.getPathInfo();
Assert.exists(pathInfo, "String pathInfo");
if (pathInfo.length() > 1 && pathInfo.endsWith("/")) {
/* NOTE: ServletAPI specifies, pathInfo may be empty or will
* start with a '/' character. It currently carries a
* trailing '/' if a "virtual" page, i.e. not a real jsp, but
* result of a servlet mapping. But Application requires url
* NOT to end with a trailing '/' for legacy free applications. */
pathInfo = pathInfo.substring(0, pathInfo.length() - 1);
}
final Page page = pages.get(pathInfo);
if (page == null) {
sresp.sendError(404, "No such page for path " + pathInfo);
} else {
final Document doc = page.buildDocument(sreq, sresp);
Templating.getPresentationManager().servePage(doc, sreq, sresp);
}
}
/**
* Adds one pair of Url - Page to the internal hash map, used as a cache.
*
* @param pathInfo url stub for a page to display
* @param page Page object to display
*/
private void addPage(final String pathInfo, final Page page) {
Assert.exists(pathInfo, String.class);
Assert.exists(page, Page.class);
// Current Implementation requires pathInfo to start with a leading '/'
// SUN Servlet API specifies: "PathInfo *may be empty* or will start
// with a '/' character."
Assert.isTrue(pathInfo.charAt(0) == '/', "path starts not with '/'");
pages.put(pathInfo, page);
}
/**
* Index page for the admin section
*/
private Page buildAdminIndexPage() {
final Page page = PageFactory.buildPage("admin", PAGE_TITLE_LABEL);
page.addGlobalStateParam(USER_ID_PARAM);
page.addGlobalStateParam(GROUP_ID_PARAM);
page.addGlobalStateParam(APPLICATIONS_ID_PARAM);
/*
* Create User split panel.
* Note: Will change soon.
*/
//final AdminSplitPanel userSplitPanel = new AdminSplitPanel(USER_NAVBAR_TITLE);
// final UserBrowsePane browsePane = new UserBrowsePane();
// userSplitPanel.addTab(USER_TAB_SUMMARY, new UserSummaryPane(userSplitPanel, browsePane));
// userSplitPanel.addTab(USER_TAB_BROWSE, browsePane);
// userSplitPanel.addTab(USER_TAB_SEARCH, new UserSearchPane(userSplitPanel, browsePane));
// userSplitPanel.addTab(USER_TAB_CREATE_USER, new CreateUserPane(userSplitPanel));
// Create the Admin's page tab bar
final TabbedPane tabbedPane = new TabbedPane();
tabbedPane.setIdAttr("page-body");
/**
* Create and add info tab
*/
//tabbedPane.addTab(INFO_TAB_TITLE, new AdminInfoTab());
/*
* Create and add the user and group tabs.
*/
//tabbedPane.addTab(USER_TAB_TITLE, userSplitPanel);
final GroupAdministrationTab groupAdminTab
= new GroupAdministrationTab();
tabbedPane.addTab(USER_TAB_TITLE, new UserAdministrationTab(tabbedPane,
groupAdminTab));
tabbedPane.addTab(GROUP_TAB_TITLE, groupAdminTab);
/*
* Create application administration panel
*/
// ToDo tabbedPane.addTab(APPLICATIONS_TAB_TITLE,
// ToDo new ApplicationsAdministrationTab());
// browsePane.setTabbedPane(tabbedPane);
// browsePane.setGroupAdministrationTab(groupAdminTab);
//Add System information tab
// ToDo tabbedPane.addTab(SYSINFO_TAB_TITLE, new SystemInformationTab());
page.add(tabbedPane);
page.lock();
return page;
}
}

View File

@ -0,0 +1,211 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.List;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.list.ListCellRenderer;
import com.arsdigita.bebop.list.ListModel;
import com.arsdigita.bebop.list.ListModelBuilder;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.EmailAddress;
import org.libreccm.core.User;
import org.libreccm.core.UserRepository;
import static com.arsdigita.ui.admin.AdminConstants.*;
import java.math.BigDecimal;
import java.util.Iterator;
/**
* Used to display and manage the list of additional email addresses
* for a user.
*/
class EmailList extends List
implements ListCellRenderer,
AdminConstants,
ActionListener
{
/**
* Constructor
*/
public EmailList() {
setModelBuilder(new EmailListModelBuilder());
setCellRenderer(this);
addActionListener(this);
}
/**
*
* @param list
* @param state
* @param value
* @param key
* @param index
* @param isSelected
* @return
*/
@Override
public Component getComponent(List list,
PageState state,
Object value,
String key,
int index,
boolean isSelected)
{
SimpleContainer c = new SimpleContainer();
if (value != null) {
ControlLink link =
new ControlLink(USER_FORM_DELETE_ADDITIONAL_EMAIL);
link.setClassAttr("deleteLink");
c.add(new Label(value.toString()));
c.add(link);
}
return c;
}
/**
* This actionlister is executed when the user clicks the "delete"
* link next to an email address.
*/
@Override
public void actionPerformed (final ActionEvent event) {
final PageState state = event.getPageState();
final Long userId = (Long) state.getValue(USER_ID_PARAM);
if (userId != null) {
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final User user = userRepository.findById(userId);
if (user == null) {
return;
} else {
final String email = (String) getSelectedKey(state);
for(EmailAddress addr : user.getEmailAddresses()) {
if (addr.getAddress().equals(email)) {
user.removeEmailAddress(addr);
}
}
userRepository.save(user);
}
}
}
}
/**
*
*
*/
class EmailListModelBuilder extends LockableImpl
implements ListModelBuilder,
AdminConstants
{
/**
*
*/
private class EmailListModel implements ListModel {
private Iterator m_emails;
private EmailAddress m_currentEmail;
/**
*
* @param emails
*/
public EmailListModel(Iterator emails) {
m_emails = emails;
}
/**
*
* @return
*/
public boolean next() {
if (m_emails.hasNext()) {
m_currentEmail = (EmailAddress) m_emails.next();
return true;
} else {
return false;
}
}
/**
*
* @return
*/
@Override
public String getKey() {
return m_currentEmail.getAddress();
}
@Override
public Object getElement() {
return m_currentEmail.getAddress();
}
}
/**
*
* @param l
* @param state
* @return
*/
@Override
public ListModel makeModel(List l, PageState state) {
final Long userId = (Long) state.getValue(USER_ID_PARAM);
if (userId == null) {
return null;
} else {
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final User user = userRepository.findById(userId);
return new EmailListModel(user.getEmailAddresses().iterator());
}
}
}

View File

@ -0,0 +1,204 @@
package com.arsdigita.ui.admin;
import java.math.BigDecimal;
import org.apache.log4j.Logger;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.RequestLocal;
import com.arsdigita.bebop.SegmentedPanel;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.Tree;
import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.Group;
import org.libreccm.core.GroupRepository;
import static com.arsdigita.ui.admin.AdminConstants.*;
/**
* Series of screens required for adding existing groups as subgroups - based on
* existing functionality for adding permissions to a folder in content/admin
*
* @version $Id: ExistingGroupAddPane.java,v 1.4 2004/06/21 11:34:03 cgyg9330
* Exp $
*/
public class ExistingGroupAddPane extends SimpleContainer implements
AdminConstants {
private static final Logger s_log = Logger.getLogger(
ExistingGroupAddPane.class);
private ParameterModel searchString = new StringParameter(SEARCH_QUERY);
private GroupSearchForm groupSearchForm;
private SimpleContainer selectGroupsPanel;
private SimpleContainer noResultsPanel;
private Tree groupTree;
private GroupAdministrationTab parentPage;
/**
*
*/
private RequestLocal parentGroup = new RequestLocal() {
@Override
protected Object initialValue(final PageState ps) {
String key = (String) groupTree.getSelectedKey(ps);
Group group = null;
if (key != null) {
final Long id = new Long(key);
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
group = groupRepository.findById(id);
}
return group;
}
};
/**
* Constructor.
*
* @param groupTree
* @param parentPage
*/
public ExistingGroupAddPane(Tree groupTree,
GroupAdministrationTab parentPage) {
this.groupTree = groupTree;
this.parentPage = parentPage;
}
/**
*
* @param p
*/
@Override
public void register(Page p) {
super.register(p);
add(getGroupSearchForm());
add(getSelectGroupsPanel());
add(getNoSearchResultPanel());
// set initial visibility of components
p.setVisibleDefault(getGroupSearchForm(), true);
p.setVisibleDefault(getSelectGroupsPanel(), false);
p.setVisibleDefault(getNoSearchResultPanel(), false);
p.addGlobalStateParam(searchString);
}
/**
*
* @return
*/
public GroupSearchForm getGroupSearchForm() {
if (groupSearchForm == null) {
groupSearchForm = new GroupSearchForm(this);
}
return groupSearchForm;
}
/**
* Returns a panel with a set of checkboxes for groups fulfilling search
* criteria
*/
public SimpleContainer getSelectGroupsPanel() {
if (selectGroupsPanel == null) {
SelectGroups selectGroups = new SelectGroups(this,
getGroupSearchForm());
selectGroupsPanel = selectGroups.getPanel();
}
return selectGroupsPanel;
}
/**
* Returns a bebop panel indicating that the user search yielded no results.
*/
public SimpleContainer getNoSearchResultPanel() {
if (noResultsPanel == null) {
Label errorMsg = GROUP_NO_RESULTS;
errorMsg.setClassAttr("errorBullet");
BoxPanel bp = new BoxPanel();
bp.add(errorMsg);
bp.add(new GroupSearchForm(this));
noResultsPanel = new SegmentedPanel().addSegment(new Label(" "), bp);
}
return noResultsPanel;
}
/**
* Shows panel with no results to user search.
*/
public void showNoResults(PageState s) {
getGroupSearchForm().setVisible(s, false);
getSelectGroupsPanel().setVisible(s, false);
getNoSearchResultPanel().setVisible(s, true);
}
/**
* Show the select groups to add as subgroups panel
*/
public void showGroups(PageState s) {
getGroupSearchForm().setVisible(s, false);
getSelectGroupsPanel().setVisible(s, true);
getNoSearchResultPanel().setVisible(s, false);
}
/**
*
* show the search form
*/
public void showSearch(PageState s) {
getGroupSearchForm().setVisible(s, true);
getSelectGroupsPanel().setVisible(s, false);
getNoSearchResultPanel().setVisible(s, false);
}
/**
*
* @return
*/
public ParameterModel getSearchString() {
return searchString;
}
/**
*
* @return
*/
public GroupAdministrationTab getParentPage() {
return parentPage;
}
/**
*
* @param ps
*
* @return
*/
public Group getParentGroup(PageState ps) {
return (Group) parentGroup.get(ps);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2013 Jens Pelzetter
*
* 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.ui.admin;
import com.arsdigita.globalization.GlobalizedMessage;
/**
* Globalization Util for admin classes
*
* @author Jens Pelzetter <jens@jp-digital.de>
* @version $Id$
*/
public class GlobalizationUtil {
private static final String BUNDLE_NAME = "com.arsdigita.ui.admin.AdminResources";
public static GlobalizedMessage globalize(final String key) {
return new GlobalizedMessage(key, BUNDLE_NAME);
}
public static GlobalizedMessage globalize(final String key, final Object[] args) {
return new GlobalizedMessage(key, BUNDLE_NAME, args);
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Tree;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.Group;
import org.libreccm.core.GroupRepository;
import static com.arsdigita.ui.admin.AdminConstants.*;
import java.math.BigDecimal;
import javax.mail.internet.InternetAddress;
/**
* Add group form.
*
* @author David Dao
* @version $Id$
*/
class GroupAddForm extends GroupForm implements FormProcessListener {
private Tree m_groupTree;
private GroupAdministrationTab m_groupTab;
public GroupAddForm(final Tree groupTree,
final GroupAdministrationTab tab) {
super(GROUP_FORM_ADD);
addProcessListener(this);
m_groupTree = groupTree;
m_groupTab = tab;
}
/**
* Processes the form.
*/
@Override
public void process(final FormSectionEvent event)
throws FormProcessException {
PageState ps = event.getPageState();
// Get super parent group.
String key = (String) m_groupTree.getSelectedKey(ps);
final Group parentGroup = null;
// if (key != null) {
// BigDecimal parentID = new BigDecimal(key);
//
// try {
// parentGroup = new Group(parentID);
// } catch (DataObjectNotFoundException exc) {
// // Parent group does not exist.
// // This is normal behavior with the new group
// // been add with no parent.
// }
// }
final Group group = new Group();
String name = (String) m_name.getValue(ps);
group.setName(name);
// Workaround for bug #189720: there is no way to remove a
// Party's primary email address, so we set it directly to
// null if it's value on the form is null.
// InternetAddress email = (InternetAddress) m_email.getValue(ps);
// if (email != null) {
// group.setPrimaryEmail(new EmailAddress(email.getAddress()));
// } else {
// //group.set("primaryEmail", null);
// group.setPrimaryEmail(null);
// }
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
groupRepository.save(group);
// if (parentGroup != null) {
// parentGroup.addSubgroup(group);
// parentGroup.save();
// }
if (m_groupTab != null) {
m_groupTab.setGroup(ps, group);
}
}
}

View File

@ -0,0 +1,685 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.List;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.RequestLocal;
import com.arsdigita.bebop.SegmentedPanel;
import com.arsdigita.bebop.Tree;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.ChangeEvent;
import com.arsdigita.bebop.event.ChangeListener;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.list.ListCellRenderer;
import com.arsdigita.bebop.list.ListModel;
import com.arsdigita.bebop.list.ListModelBuilder;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.toolbox.ui.LayoutPanel;
import static com.arsdigita.ui.admin.AdminConstants.*;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.util.UncheckedWrapperException;
import java.math.BigDecimal;
import java.util.ArrayList;
import org.apache.log4j.Logger;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.Group;
import org.libreccm.core.GroupRepository;
/**
* Constructs the panel for administration of groups.
*
* @author David Dao
*
*/
class GroupAdministrationTab extends LayoutPanel implements AdminConstants,
ChangeListener {
private static final Logger LOGGER = Logger.getLogger(
GroupAdministrationTab.class);
private final Tree groupTree;
private SearchAndList subMemberSearch;
private ActionLink addSubmemberLink;
private final Component groupInfoPanel;
private final Component subGroupPanel;
private final Component subMemberPanel;
private final Component extremeActionPanel;
private final Component groupAddPanel;
private final Component groupEditPanel;
private final Component groupDeleteFailedPanel;
private final Component existingGroupAddPanel;
private ExistingGroupAddPane m_existingGroupAdd;
private final java.util.List<Component> panelList
= new ArrayList<Component>();
private final RequestLocal requestLocalGroup;
/**
*
* @param page
*/
@Override
public void register(final Page page) {
for (int i = 0; i < panelList.size(); i++) {
page.setVisibleDefault(panelList.get(i), false);
}
page.setVisibleDefault(groupAddPanel, true);
}
/**
*
* @param state
*
* @return
*/
public Group getGroup(final PageState state) {
return (Group) requestLocalGroup.get(state);
}
public void setGroup(final PageState state, final Group group) {
final String groupId = Long.toString(group.getSubjectId());
requestLocalGroup.set(state, group);
groupTree.setSelectedKey(state, groupId);
if (!"-1".equals(groupId)) {
expandGroups(state, group);
groupTree.expand("-1", state);
}
}
private void expandGroups(final PageState state, final Group group) {
// groupTree.expand(Long.toString(group.getSubjectId()), state);
//
// final List< superGroups = group.getSupergroups();
// Group superGroup;
// while (superGroups.next()) {
// superGroup = (Group) superGroups.getDomainObject();
// expandGroups(state, superGroup);
// }
}
/**
* Constructor
*/
public GroupAdministrationTab() {
super();
setClassAttr("sidebarNavPanel");
setAttribute("navbar-title", "Groups");
requestLocalGroup = new RequestLocal() {
@Override
protected Object initialValue(final PageState state) {
String key = (String) groupTree.getSelectedKey(state);
Group group;
if (key != null) {
final long id = Long.parseLong(key);
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil
.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
group = groupRepository.findById(id);
return group;
}
return null;
}
};
setClassAttr("navbar");
groupTree = new Tree(new GroupTreeModelBuilder());
groupTree.addChangeListener(this);
setLeft(groupTree);
final SegmentedPanel body = new SegmentedPanel();
body.setClassAttr("main");
groupInfoPanel = buildGroupInfoPanel(body);
panelList.add(groupInfoPanel);
groupEditPanel = buildGroupEditPanel(body);
panelList.add(groupEditPanel);
subGroupPanel = buildSubGroupPanel(body);
panelList.add(subGroupPanel);
groupAddPanel = buildGroupAddPanel(body);
panelList.add(groupAddPanel);
existingGroupAddPanel = buildExistingGroupAddPanel(body);
panelList.add(existingGroupAddPanel);
subMemberPanel = buildMemberListPanel(body);
panelList.add(subMemberPanel);
extremeActionPanel = buildExtremeActionPanel(body);
panelList.add(extremeActionPanel);
groupDeleteFailedPanel = buildGroupDeleteFailedPanel(body);
panelList.add(groupDeleteFailedPanel);
setBody(body);
}
public void displayAddGroupPanel(final PageState state) {
hideAll(state);
groupAddPanel.setVisible(state, true);
}
private void displayAddExistingGroupPanel(final PageState state) {
hideAll(state);
existingGroupAddPanel.setVisible(state, true);
}
public void displayEditPanel(final PageState state) {
hideAll(state);
groupEditPanel.setVisible(state, true);
}
public void displayGroupInfoPanel(final PageState state) {
showAll(state);
groupEditPanel.setVisible(state, false);
groupAddPanel.setVisible(state, false);
groupDeleteFailedPanel.setVisible(state, false);
existingGroupAddPanel.setVisible(state, false);
}
public void displayDeleteFailedPanel(final PageState state) {
hideAll(state);
groupDeleteFailedPanel.setVisible(state, true);
}
/**
*
* @param event
*/
public void stateChanged(final ChangeEvent event) {
final PageState ps = event.getPageState();
final String key = (String) groupTree.getSelectedKey(ps);
// added cg - reset existing group add panel to the search screen
// when a new group is selected from the tree
m_existingGroupAdd.showSearch(ps);
if (key == null || key.equals("-1")) {
/**
* If root node is selected then display add panel only.
*/
displayAddGroupPanel(ps);
} else {
displayGroupInfoPanel(ps);
}
ps.setValue(GROUP_ID_PARAM, new BigDecimal(key));
}
/**
* Build a panel to display group basic information.
*/
private Component buildGroupInfoPanel(final SegmentedPanel main) {
final BoxPanel body = new BoxPanel();
//body.add(new GroupInfo(this));
final ColumnPanel infoPanel = new ColumnPanel(2);
infoPanel.add(new Label(new GlobalizedMessage("ui.admin.groups.name",
BUNDLE_NAME)));
final Label nameLabel = new Label();
nameLabel.addPrintListener(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
final Label target = (Label) event.getTarget();
final PageState state = event.getPageState();
final Group group = getGroup(state);
target.setLabel(group.getName());
}
});
infoPanel.add(nameLabel);
body.add(infoPanel);
ActionLink link = new ActionLink(EDIT_GROUP_LABEL);
link.setClassAttr("actionLink");
link.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PageState ps = e.getPageState();
displayEditPanel(ps);
}
});
body.add(link);
return main.addSegment(GROUP_INFORMATION_HEADER, body);
}
/**
* Build group edit form.
*/
private Component buildGroupEditPanel(final SegmentedPanel main) {
return main.addSegment(GROUP_EDIT_HEADER, new GroupEditForm(this));
}
/**
* Build panel to display direct subgroup information.
*/
private Component buildSubGroupPanel(final SegmentedPanel main) {
final BoxPanel body = new BoxPanel();
final BoxPanel labelStatus = new BoxPanel(BoxPanel.HORIZONTAL);
labelStatus.add(SUBGROUP_COUNT_LABEL);
final Label countLabel = new Label("");
countLabel.addPrintListener(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
// final PageState ps = event.getPageState();
//
// final Label target = (Label) event.getTarget();
// Group g = getGroup(ps);
// if (g != null) {
// target.setLabel(String.valueOf(g.countSubgroups()));
// }
}
});
final ActionLink status = new ActionLink(countLabel);
status.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
PageState ps = event.getPageState();
String key = (String) groupTree.getSelectedKey(ps);
groupTree.expand(key, ps);
}
});
labelStatus.add(status);
body.add(labelStatus);
final List subGroupList = new List(new SubGroupListModelBuilder(this));
subGroupList.setCellRenderer(new ListCellRenderer() {
@Override
public Component getComponent(final List list,
final PageState state,
final Object value,
final String key,
final int index,
final boolean isSelected) {
final BoxPanel b = new BoxPanel(BoxPanel.HORIZONTAL);
b.add(new Label(((Group) value).getName()));
final ControlLink removeLink = new ControlLink(
REMOVE_SUBGROUP_LABEL);
removeLink.setClassAttr("actionLink");
b.add(removeLink);
return b;
}
});
subGroupList.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
final PageState state = event.getPageState();
final String key = (String) ((List) event.getSource())
.getSelectedKey(state);
if (key != null) {
final Long groupId = Long.parseLong(key);
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil
.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
final Group group = groupRepository.findById(groupId);
final Group parent = getGroup(state);
if (parent != null) {
groupRepository.save(parent);
}
final BigDecimal groupID = new BigDecimal(key);
// try {
// final Group group = new Group(groupID);
// final Group parent = getGroup(state);
// if (parent != null) {
// parent.removeSubgroup(group);
// parent.save();
// }
// } catch (DataObjectNotFoundException exc) {
// }
}
}
});
body.add(subGroupList);
final ActionLink addLink = new ActionLink(ADD_SUBGROUP_LABEL);
addLink.setClassAttr("actionLink");
addLink.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent event) {
PageState ps = event.getPageState();
displayAddGroupPanel(ps);
}
});
body.add(addLink);
// new actionlink and anonymous ActionListener class added cg
final ActionLink addExistingLink = new ActionLink(
ADD_EXISTING_GROUP_TO_SUBGROUPS_LABEL);
addExistingLink.setClassAttr("actionLink");
addExistingLink.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
LOGGER.debug("Add existing group link pressed");
PageState ps = event.getPageState();
displayAddExistingGroupPanel(ps);
}
});
body.add(addExistingLink);
return main.addSegment(SUBGROUP_HEADER, body);
}
/**
* Build group add form.
*/
private Component buildGroupAddPanel(final SegmentedPanel main) {
return main.addSegment(ADD_GROUP_LABEL,
new GroupAddForm(groupTree, this));
}
private Component buildExistingGroupAddPanel(final SegmentedPanel main) {
m_existingGroupAdd = new ExistingGroupAddPane(groupTree, this);
return main.addSegment(ADD_EXISTING_GROUP_TO_SUBGROUPS_LABEL,
m_existingGroupAdd);
}
/**
* Build group's member panel.
*/
private Component buildMemberListPanel(final SegmentedPanel main) {
BoxPanel body = new BoxPanel() {
@Override
public void register(final Page page) {
page.setVisibleDefault(subMemberSearch, false);
}
};
// body.add(new SubMemberPanel(this));
//
// addSubmemberLink = new ActionLink(ADD_SUBMEMBER_LABEL);
// addSubmemberLink.setClassAttr("actionLink");
// addSubmemberLink.addActionListener(new ActionListener() {
//
// @Override
// public void actionPerformed(final ActionEvent event) {
// PageState ps = event.getPageState();
// addSubmemberLink.setVisible(ps, false);
// subMemberSearch.setVisible(ps, true);
// }
//
// });
//
// subMemberSearch = new SearchAndList("searchsubmember");
// subMemberSearch.setListModel(new UserSearchAndListModel());
// subMemberSearch.addChangeListener(new ChangeListener() {
//
// @Override
// public void stateChanged(final ChangeEvent event) {
// PageState ps = event.getPageState();
//
// String key = (String) subMemberSearch.getSelectedKey(ps);
// if (key != null) {
// final BigDecimal userID = new BigDecimal(key);
//
// final Group group = getGroup(ps);
//
// if (group != null) {
// try {
// User user = User.retrieve(userID);
// group.addMember(user);
// group.save();
// } catch (DataObjectNotFoundException exc) {
// // Ignore if user id is not valid
// } catch (PersistenceException pexc) {
// // Display error message that user
// // already existed in group.
// }
// }
// }
// subMemberSearch.reset(ps);
// subMemberSearch.setVisible(ps, false);
// addSubmemberLink.setVisible(ps, true);
// }
//
// });
//
// body.add(subMemberSearch);
// body.add(addSubmemberLink);
return main.addSegment(SUBMEMBER_HEADER, body);
}
/**
* Build extreme action panel.
*/
private Component buildExtremeActionPanel(final SegmentedPanel main) {
final BoxPanel body = new BoxPanel();
final ActionLink deleteLink = new ActionLink(DELETE_GROUP_LABEL);
deleteLink.setClassAttr("actionLink");
deleteLink.setConfirmation(GROUP_DELETE_CONFIRMATION);
deleteLink.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
PageState ps = event.getPageState();
final Group group = (Group) requestLocalGroup.get(ps);
if (group != null) {
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil.findBean(GroupRepository.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
groupRepository.delete(group);
// try {
// group.delete();
// groupTree.setSelectedKey(ps, "-1");
// } catch (PersistenceException exc) {
// LOGGER.warn("Error deleting subgroup", exc);
// displayDeleteFailedPanel(ps);
// }
}
// Select root node
}
});
body.add(deleteLink);
return main.addSegment(GROUP_EXTREME_ACTIONS_HEADER,
body);
}
/**
* Build a panel to display an error message when unable to delete group.
*/
private Component buildGroupDeleteFailedPanel(final SegmentedPanel main) {
final ActionLink link = new ActionLink(GROUP_ACTION_CONTINUE);
link.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent event) {
PageState ps = event.getPageState();
displayGroupInfoPanel(ps);
}
});
link.setClassAttr("actionLink");
final Label label = new Label(GROUP_DELETE_FAILED_MSG);
label.setClassAttr("deleteFailedMessage");
final BoxPanel panel = new BoxPanel();
panel.add(label);
panel.add(link);
return main.addSegment(GROUP_DELETE_FAILED_HEADER, panel);
}
/**
* Hides all components of the in preparation for turning selected
* components back on.
*/
private void hideAll(final PageState state) {
for (int i = 0; i < panelList.size(); i++) {
((Component) panelList.get(i)).setVisible(state, false);
}
}
/**
* Show all components of the in preparation for turning visibility of
* selected components off .
*/
private void showAll(final PageState state) {
for (int i = 0; i < panelList.size(); i++) {
((Component) panelList.get(i)).setVisible(state, true);
}
}
}
class SubGroupListModelBuilder extends LockableImpl implements ListModelBuilder {
private final GroupAdministrationTab parent;
public SubGroupListModelBuilder(final GroupAdministrationTab parent) {
this.parent = parent;
}
public ListModel makeModel(final List list, final PageState state) {
final Group group = parent.getGroup(state);
// if (group != null) {
// return new SubGroupListModel(group.getSubgroups());
// }
return new SubGroupListModel(null);
}
}
/**
* CLASS
*
*/
class SubGroupListModel implements ListModel {
// private GroupCollection m_coll;
/**
*
* @param collection
*/
public SubGroupListModel(final Object collection) {
// m_coll = collection;
// m_coll.addOrder("lower(" + Group.DISPLAY_NAME + ") asc");
}
/**
*
* @return
*/
public Object getElement() {
// return m_coll.getGroup();
return null;
}
/**
*
* @return
*/
public String getKey() {
// return m_coll.getID().toString();
return null;
}
/**
*
* @return
*/
public boolean next() {
// if (m_coll != null) {
// return m_coll.next();
// }
return false;
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.event.FormInitListener;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.PageState;
import java.math.BigDecimal;
import javax.mail.internet.InternetAddress;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.Group;
import org.libreccm.core.GroupRepository;
import static com.arsdigita.ui.admin.AdminConstants.*;
/**
* Edit group form.
*
* @author David Dao
* @version $Id$
*/
class GroupEditForm extends GroupForm implements FormInitListener,
FormProcessListener {
private GroupAdministrationTab m_parent;
public GroupEditForm() {
this(null);
}
public GroupEditForm(final GroupAdministrationTab parent) {
super(GROUP_FORM_EDIT);
addInitListener(this);
addProcessListener(this);
m_parent = parent;
}
/**
* Initializes form elements by retrieving their values from the database.
*/
@Override
public void init(final FormSectionEvent event) {
final PageState state = event.getPageState();
final Long id = (Long) state.getValue(USER_ID_PARAM);
if (id != null) {
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil.findBean(
GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
final Group group = groupRepository.findById(id);
m_name.setValue(state, group.getName());
}
}
/**
* Processes the form.
*/
@Override
public void process(final FormSectionEvent event)
throws FormProcessException {
final PageState state = event.getPageState();
final Long id = (Long) state.getValue(GROUP_ID_PARAM);
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
if (id == null) {
throw new FormProcessException(GlobalizationUtil.globalize(
"ui.admin.groups.ID_is_null"));
}
final Group group = groupRepository.findById(id);
if (group == null) {
throw new FormProcessException(GlobalizationUtil.globalize(
"ui.admin.groups.couldnt_find_specified_group"));
}
final String name = (String) m_name.getValue(state);
group.setName(name);
groupRepository.save(group);
if (m_parent != null) {
m_parent.displayGroupInfoPanel(state);
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.parameters.EmailParameter;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.ColumnPanel;
import static com.arsdigita.ui.admin.AdminConstants.*;
/**
* Base form for add/edit group.
*
* @author David Dao
* @version $Id$
*/
class GroupForm extends Form {
protected TextField m_name;
// protected TextField m_email;
public GroupForm(String formName) {
super(formName);
m_name = new TextField(new StringParameter(GROUP_FORM_INPUT_NAME));
m_name.setMaxLength(200);
m_name.addValidationListener(new NotEmptyValidationListener());
m_name.addValidationListener(new StringLengthValidationListener (200));
add(GROUP_FORM_LABEL_NAME);
add(m_name);
// m_email = new TextField(new EmailParameter(GROUP_FORM_INPUT_PRIMARY_EMAIL));
// m_email.setMaxLength(100);
// add(GROUP_FORM_LABEL_PRIMARY_EMAIL);
// add(m_email);
// Submit button
add(new Submit(GROUP_FORM_SUBMIT), ColumnPanel.CENTER |
ColumnPanel.FULL_WIDTH);
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2006 Chris Gilbert. 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.ui.admin;
import java.util.List;
import org.apache.log4j.Logger;
import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.bebop.parameters.StringParameter;
import static com.arsdigita.ui.admin.AdminConstants.*;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.Group;
import org.libreccm.core.GroupRepository;
import java.util.Collections;
/**
* @author cgyg9330
*
* Search for groups to add as subgroups. Text entered by user is put into a
* groupName like '%......%' query, which also excludes problematic results
* (groups that are already supergroups or subgroups of the current group
*
*/
public class GroupSearchForm extends Form implements FormProcessListener,
AdminConstants {
private ExistingGroupAddPane parentPane;
private TextField m_search;
private List<Group> results = null;
private static final Logger s_log = Logger.getLogger(GroupSearchForm.class);
public GroupSearchForm(ExistingGroupAddPane parent) {
super("SearchGroups", new SimpleContainer());
parentPane = parent;
setMethod(Form.POST);
addProcessListener(this);
add(GROUP_SEARCH_LABEL);
add(new Label("&nbsp;", false));
StringParameter searchParam = new StringParameter(SEARCH_QUERY);
m_search = new TextField(searchParam);
m_search.addValidationListener(new NotEmptyValidationListener());
m_search.setSize(20);
add(m_search, ColumnPanel.RIGHT);
Submit submit = new Submit("submit");
submit.setButtonLabel(SEARCH_BUTTON);
add(submit, ColumnPanel.LEFT);
}
@Override
public void process(final FormSectionEvent event)
throws FormProcessException {
PageState state = event.getPageState();
Group parent = parentPane.getParentGroup(state);
String search = (String) m_search.getValue(state);
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
results = groupRepository.searchGroupByName(search);
if (results.isEmpty()) {
parentPane.showNoResults(state);
} else {
// put search string into Page
state.setValue(getSearchString(), m_search.getValue(state));
parentPane.showGroups(state);
}
}
/**
*
* allow other classes to get hold of the results, to avoid constructing the
* same query in several places
*
* @return
*/
public List<Group> getResults() {
return Collections.unmodifiableList(results);
}
private ParameterModel getSearchString() {
return parentPane.getSearchString();
}
}

View File

@ -0,0 +1,164 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.tree.TreeModel;
import com.arsdigita.bebop.tree.TreeNode;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.Group;
import org.libreccm.core.GroupRepository;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
/**
*
*
* @author David Dao
*
*/
public class GroupTreeModel implements TreeModel {
// private class GroupIterator implements Iterator {
//
// private List< m_coll;
//
// public GroupIterator(ACSObjectCollection coll) {
// m_coll = coll;
// }
//
// public boolean hasNext() {
// return m_coll.next();
// }
//
// public Object next() {
// return new GroupTreeNode(m_coll.getACSObject());
// }
//
// public void remove() {
// throw new UnsupportedOperationException();
// }
//
// }
/**
* Obtain the root folder of the tree
*
* @param state
*
* @return
*/
@Override
public TreeNode getRoot(final PageState state) {
return new RootTreeNode();
}
/**
* Check whether a given node has children
*
* @param node
* @param state
*
* @return
*/
@Override
public boolean hasChildren(final TreeNode node, final PageState state) {
if (node instanceof RootTreeNode) {
return true;
} else {
return false;
}
}
/**
* Get direct children in this node.
*
* @param node
* @param state
*
* @return
*/
@Override
public Iterator<Group> getChildren(final TreeNode node,
final PageState state) {
if (node instanceof RootTreeNode) {
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(
"Failed to lookup GroupRepository", ex);
}
final List<Group> groups = groupRepository.findAll();
return groups.iterator();
} else {
return null;
}
}
}
class RootTreeNode implements TreeNode {
@Override
public Object getKey() {
return "-1";
}
@Override
public Object getElement() {
return "/";
}
}
class GroupTreeNode implements TreeNode {
private String m_key;
private String m_name;
public GroupTreeNode(Group group) {
m_key = Long.toString(group.getSubjectId());
m_name = group.getName();
}
@Override
public Object getKey() {
return m_key;
}
@Override
public Object getElement() {
return m_name;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2013 Jens Pelzetter
*
* 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.ui.admin;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Tree;
import com.arsdigita.bebop.tree.TreeModel;
import com.arsdigita.bebop.tree.TreeModelBuilder;
import com.arsdigita.util.LockableImpl;
/**
*
* @author Jens Pelzetter <jens@jp-digital.de>
* @version $Id$
*/
public class GroupTreeModelBuilder extends LockableImpl implements TreeModelBuilder {
public TreeModel makeModel(final Tree tree, final PageState state) {
tree.expand("-1", state);
return new GroupTreeModel();
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.list.ListModel;
import org.libreccm.core.Subject;
import java.util.List;
/**
*
* @version $Id$
*/
class PartyListModel implements ListModel {
private final List<Subject> m_parties;
private Subject m_currentParty = null;
private int index = 0;
/**
* Constructor for the list
* Builds the list of party list model for parties
*
* @param partys the partyCollection
**/
public PartyListModel(final List<Subject> parties) {
m_parties = parties;
}
/**
* Check whether is an another party
*
* @return true if another party exist, false otherwise
**/
@Override
public boolean next() {
if (index < m_parties.size()) {
index++;
m_currentParty = m_parties.get(index);
return true;
} else {
return false;
}
}
/**
* Returns the unqiue ID string for current Party
*
* @return the unqiue ID string for current Party
**/
@Override
public String getKey() {
return Long.toString(m_currentParty.getSubjectId());
}
/**
* Returns the current Party
*
* @return the current Party
**/
@Override
public Object getElement() {
return m_currentParty.getSubjectId();
}
}

View File

@ -0,0 +1,189 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.List;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Resettable;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.list.ListCellRenderer;
import com.arsdigita.bebop.list.ListModel;
import com.arsdigita.bebop.list.ListModelBuilder;
import com.arsdigita.bebop.parameters.NotNullValidationListener;
import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.bebop.event.ChangeListener;
import static com.arsdigita.ui.admin.AdminConstants.*;
/**
*
* @author David Dao
*/
class SearchAndList extends SimpleContainer
implements AdminConstants,
Resettable {
/**
* String catalog.
*/
private static final String FORM_INPUT_NAME = "query";
private static final GlobalizedMessage LABEL_SUBMIT = new GlobalizedMessage(
"ui.admin.searchAndList.submit",
BUNDLE_NAME);
private static final GlobalizedMessage SEARCH_AGAIN = new GlobalizedMessage(
"ui.admin.searchAndList.submitAgain",
BUNDLE_NAME);
private Form m_searchForm;
private List m_searchResultList;
private ParameterModel m_queryModel;
private SearchAndListModel m_listModel;
private SimpleContainer m_searchResultContainer;
private class SearchListModelBuilder extends LockableImpl
implements ListModelBuilder {
public ListModel makeModel(List l, PageState state) {
return m_listModel;
}
}
private ListModelBuilder m_listModelBuilder = new SearchListModelBuilder();
private FormProcessListener m_formProcessListener
= new FormProcessListener() {
public void process(FormSectionEvent e) throws FormProcessException {
FormData data = e.getFormData();
PageState state = e.getPageState();
String query = (String) data.get(FORM_INPUT_NAME);
boolean visible = false;
if (query == null || query.equals("")) {
visible = true;
} else {
visible = false;
}
m_listModel.setQuery(query);
m_searchForm.setVisible(state, visible);
m_searchResultContainer.setVisible(state, !visible);
}
};
public SearchAndList(String name) {
super();
/**
* Create a search form.
*/
m_searchForm = new Form(name, new BoxPanel(BoxPanel.HORIZONTAL));
m_queryModel = new StringParameter(FORM_INPUT_NAME);
TextField query = new TextField(m_queryModel);
query.addValidationListener(new NotNullValidationListener());
m_searchForm.add(query);
m_searchForm.add(new Submit(LABEL_SUBMIT));
m_searchForm.addProcessListener(m_formProcessListener);
add(m_searchForm);
/**
* Create a search result container.
*/
m_searchResultContainer = new SimpleContainer();
add(m_searchResultContainer);
m_searchResultList = new List();
m_searchResultList.setClassAttr("SearchResultList");
m_searchResultList.setModelBuilder(m_listModelBuilder);
ActionLink link = new ActionLink(new Label(SEARCH_AGAIN));
link.setClassAttr("actionLink");
link.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PageState state = e.getPageState();
m_searchForm.setVisible(state, true);
m_searchResultContainer.setVisible(state, false);
}
});
m_searchResultContainer.add(m_searchResultList);
m_searchResultContainer.add(link);
}
public void addChangeListener(ChangeListener l) {
m_searchResultList.addChangeListener(l);
}
public Object getSelectedKey(PageState ps) {
return m_searchResultList.getSelectedKey(ps);
}
public void clearSelection(PageState ps) {
m_searchResultList.clearSelection(ps);
}
public void setListModel(SearchAndListModel model) {
m_listModel = model;
}
public void setResultCellRenderer(ListCellRenderer r) {
m_searchResultList.setCellRenderer(r);
}
public void register(Page p) {
p.setVisibleDefault(m_searchForm, true);
p.setVisibleDefault(m_searchResultContainer, false);
}
public void reset(PageState ps) {
m_searchResultContainer.setVisible(ps, false);
m_searchForm.setVisible(ps, true);
clearSelection(ps);
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.list.ListModel;
/**
*
*
* @author David Dao
* @version $Id$
*/
public interface SearchAndListModel extends ListModel {
/**
* Specify the user's search.
*/
public void setQuery (String query);
}

View File

@ -0,0 +1,166 @@
/*
* Copyright (C) 2006 Chris Gilbert. 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.ui.admin;
import java.math.BigDecimal;
import java.util.TooManyListenersException;
import org.apache.log4j.Logger;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.FormSubmissionListener;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.form.CheckboxGroup;
import com.arsdigita.bebop.form.Option;
import com.arsdigita.bebop.form.OptionGroup;
import com.arsdigita.bebop.form.Submit;
import org.libreccm.core.Group;
import java.util.List;
import static com.arsdigita.ui.admin.AdminConstants.*;
/**
* @author cgyg9330
*
* contains a form that allows the user to select desired groups to be added as
* subgroups to the currently selected group
*
* NB could be improved with cancel button - currently need to use browser back
* button
*/
public class SelectGroups {
private static final Logger s_log = Logger.getLogger(SelectGroups.class);
private static final String GROUPS_CBG = "groups_cbg";
private BoxPanel groupPanel;
private CheckboxGroup groups;
private Form form;
private Submit save;
private ExistingGroupAddPane parentPane;
private GroupSearchForm searchForm;
public SelectGroups(ExistingGroupAddPane parent, GroupSearchForm searchForm) {
parentPane = parent;
makeForm();
groupPanel = new BoxPanel();
groupPanel.add(form);
this.searchForm = searchForm;
}
/**
* Builds the form used to select groups.
*/
private void makeForm() {
form = new Form("ChooseGroups", new BoxPanel());
form.setMethod(Form.POST);
form.addSubmissionListener(new AddGroupsSubmissionListener());
form.add(PICK_GROUPS);
groups = new CheckboxGroup(GROUPS_CBG);
groups.setClassAttr("vertical");
try {
groups.addPrintListener(new GroupSearchPrintListener());
} catch (TooManyListenersException e) {
throw new RuntimeException(e.getMessage());
}
form.add(groups);
save = new Submit("save", SAVE_BUTTON);
form.add(save);
}
public BoxPanel getPanel() {
return groupPanel;
}
/**
*
* FormSubmissionListener that sets selected groups to be subgroups of the
* currently selected group
*
*/
private class AddGroupsSubmissionListener
implements FormSubmissionListener {
public void submitted(FormSectionEvent e) throws FormProcessException {
PageState state = e.getPageState();
FormData data = e.getFormData();
String[] selectedGroups = (String[]) data.get(GROUPS_CBG);
BigDecimal groupID = null;
Group child = null;
// if (selectedGroups != null) {
// Group parent = parentPane.getParentGroup(state);
// for (int i = 0; i < selectedGroups.length; i++) {
// groupID = new BigDecimal(selectedGroups[i]);
// try {
// child = new Group(groupID);
// } catch (DataObjectNotFoundException e2) {
// s_log.warn("Non existant Group " + child
// + " selected to be child of " + parent);
// }
// parent.addSubgroup(child);
// parent.save();
// }
// }
parentPane.showSearch(state);
parentPane.getParentPage().displayGroupInfoPanel(state);
}
}
/**
*
* @author cgyg9330
*
* Printlistener retrieves query results from the groupsearch form and uses
* them as the entries on the checkbox group
*/
private class GroupSearchPrintListener implements PrintListener {
public void prepare(PrintEvent e) {
PageState state = e.getPageState();
OptionGroup cbg = (CheckboxGroup) e.getTarget();
List<Group> results = searchForm.getResults();
String groupID;
String groupName;
Group child;
for(Group group : results) {
child = group;
groupID = Long.toString(child.getSubjectId());
groupName = child.getName();
cbg.addOption(new Option(groupID, groupName));
}
}
}
}

View File

@ -0,0 +1,173 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.list.ListModelBuilder;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.bebop.list.ListModel;
import com.arsdigita.bebop.List;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.list.ListCellRenderer;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.Group;
import org.libreccm.core.GroupManager;
import org.libreccm.core.GroupMembership;
import org.libreccm.core.GroupRepository;
import org.libreccm.core.User;
import org.libreccm.core.UserRepository;
import static com.arsdigita.ui.admin.AdminConstants.*;
/**
*
*
* @author David Dao
*
*/
class SubMemberPanel extends BoxPanel {
private List m_memberList;
private GroupAdministrationTab m_mainTab;
public SubMemberPanel(final GroupAdministrationTab tab) {
m_mainTab = tab;
m_memberList = new List(new SubMemberListModelBuilder(tab));
m_memberList.setCellRenderer(new ListCellRenderer() {
@Override
public Component getComponent(final List list,
final PageState state,
final Object value,
final String key,
final int index,
final boolean isSelected) {
final BoxPanel panel = new BoxPanel(BoxPanel.HORIZONTAL);
Label label = new Label(((User) value).getScreenName());
panel.add(label);
ControlLink removeLink = new ControlLink(REMOVE_SUBMEMBER_LABEL);
removeLink.setClassAttr("actionLink");
panel.add(removeLink);
return panel;
}
});
m_memberList.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
final PageState state = event.getPageState();
final String key = (String) m_memberList.getSelectedKey(state);
if (key != null) {
final Long userID = new Long(key);
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
final GroupManager groupManager;
final GroupRepository groupRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
groupManager = cdiUtil.findBean(GroupManager.class);
groupRepository = cdiUtil.findBean(GroupRepository.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final User user = userRepository.findById(userID);
final Group group = m_mainTab.getGroup(state);
if (group != null) {
groupManager.removeUserFromGroup(user, group);
groupRepository.save(group);
}
}
}
});
add(m_memberList);
}
}
class SubMemberListModelBuilder extends LockableImpl
implements ListModelBuilder {
private GroupAdministrationTab m_mainTab;
public SubMemberListModelBuilder(final GroupAdministrationTab tab) {
m_mainTab = tab;
}
@Override
public ListModel makeModel(final List list, final PageState state) {
final Group group = m_mainTab.getGroup(state);
final java.util.List<GroupMembership> members;
if (group == null) {
members = null;
} else {
members = group.getMembers();
}
return new SubMemberListModel(members);
}
}
class SubMemberListModel implements ListModel {
private final java.util.List<GroupMembership> members;
private int index;
public SubMemberListModel(final java.util.List<GroupMembership> members) {
this.members = members;
}
@Override
public Object getElement() {
return members.get(index);
}
@Override
public String getKey() {
return Long.toString(members.get(index).getMembershipId());
}
@Override
public boolean next() {
if (index < members.size()) {
index++;
return true;
} else {
return false;
}
}
}

View File

@ -0,0 +1,190 @@
/*
* Copyright (c) 2013 Jens Pelzetter
*
* 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.ui.admin;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.List;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Resettable;
import com.arsdigita.bebop.TabbedPane;
import com.arsdigita.bebop.event.ChangeEvent;
import com.arsdigita.bebop.event.ChangeListener;
import com.arsdigita.bebop.list.ListModel;
import com.arsdigita.bebop.list.ListModelBuilder;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.toolbox.ui.LayoutPanel;
import static com.arsdigita.ui.admin.AdminConstants.*;
import com.arsdigita.util.Assert;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.xml.Element;
import java.util.ArrayList;
/**
*
* @author Jens Pelzetter <jens@jp-digital.de>
* @version $Id$
*/
class UserAdministrationTab extends LayoutPanel {
private final List sections;
private final java.util.List<Component> components = new ArrayList<Component>();
private final java.util.List<Label> keys = new ArrayList<Label>();
private final GlobalizedMessage title = USER_NAVBAR_TITLE;
public UserAdministrationTab(final TabbedPane parent, final GroupAdministrationTab groupAdminTab) {
super();
setClassAttr("sidebarNavPanel");
sections = new List(new GlobalizedTabModelBuilder());
sections.addChangeListener(new SectionChangeListener());
sections.setClassAttr("navbar");
setLeft(sections);
final UserBrowsePane browsePane = new UserBrowsePane();
// ToDo final UserSummarySection summarySection = new UserSummarySection(this, browsePane);
// ToDo final UserSearchSection searchSection = new UserSearchSection(this, browsePane);
// ToDo final UserCreateSection createSection = new UserCreateSection(this);
browsePane.setTabbedPane(parent);
browsePane.setGroupAdministrationTab(groupAdminTab);
final BoxPanel body = new BoxPanel();
// ToDo addSection(USER_TAB_SUMMARY, summarySection, body);
addSection(USER_TAB_BROWSE, browsePane, body);
// ToDo addSection(USER_TAB_SEARCH, searchSection, body);
// ToDo addSection(USER_TAB_CREATE_USER, createSection, body);
setBody(body);
}
/**
*
* @pre label != null && c != null
*/
private void addSection(final Label label, final Component component, final BoxPanel panel) {
Assert.isUnlocked(this);
components.add(component);
component.setClassAttr("main");
panel.add(component);
keys.add(label);
}
/**
*
* @param page
*/
@Override
public void register(final Page page) {
Assert.isUnlocked(this);
for (int i = 0; i < components.size(); i++) {
page.setVisibleDefault(components.get(i), false);
}
}
public void setSection(final int index, final PageState state) {
sections.setSelectedKey(state, String.valueOf(index));
for (int i = 0; i < components.size(); i++) {
if (i == index) {
(components.get(i)).setVisible(state, true);
((Resettable) components.get(i)).reset(state);
} else {
(components.get(i)).setVisible(state, false);
}
}
}
@Override
public void generateXML(final PageState state, final Element parent) {
super.generateXML(state, parent);
/**
* Globalized navbar title.
* Why did I override generateXML method to globalize the title?
* Because CMS put title bar as an attribute of element. This
* is the only method I could come up with.
*/
final Element child = (Element) parent.getChildren().get(0);
child.addAttribute("navbar-title",
(String) title.localize(state.getRequest()));
}
private class SectionChangeListener implements ChangeListener {
public SectionChangeListener() {
//Nothing
}
@Override
public void stateChanged(final ChangeEvent event) {
final PageState state = event.getPageState();
final int selectedIndex = Integer.parseInt((String) sections.getSelectedKey(state));
setSection(selectedIndex, state);
}
}
private class GlobalizedTabModelBuilder extends LockableImpl implements ListModelBuilder {
public GlobalizedTabModelBuilder() {
super();
}
@Override
public ListModel makeModel(final List list, final PageState state) {
return new TabNameListModel(state);
}
}
private class TabNameListModel implements ListModel {
private int index = -1;
private final PageState pageState;
public TabNameListModel(final PageState state) {
pageState = state;
}
@Override
public Object getElement() {
return keys.get(index).getLabel(pageState);
}
@Override
public String getKey() {
return String.valueOf(index);
}
public boolean next() {
final boolean result = (index < keys.size() - 1);
//return (index++ < keys.size() - 1);
index++;
return result;
}
}
}

View File

@ -0,0 +1,783 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.List;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.RequestLocal;
import com.arsdigita.bebop.Resettable;
import com.arsdigita.bebop.SegmentedPanel;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.TabbedPane;
// import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.ActionEvent;
// import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.ChangeEvent;
import com.arsdigita.bebop.event.ChangeListener;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.event.TableActionEvent;
import com.arsdigita.bebop.event.TableActionListener;
import com.arsdigita.bebop.list.ListModel;
import com.arsdigita.bebop.list.ListModelBuilder;
import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.bebop.table.TableModelBuilder;
import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.ui.UI;
import com.arsdigita.web.URL;
import com.arsdigita.web.RedirectSignal;
import static com.arsdigita.ui.admin.AdminConstants.*;
import com.arsdigita.util.LockableImpl;
import com.arsdigita.util.UncheckedWrapperException;
import java.math.BigDecimal;
import java.util.ArrayList;
import org.apache.log4j.Logger;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.CcmSessionContext;
import org.libreccm.core.Group;
import org.libreccm.core.GroupMembership;
import org.libreccm.core.GroupRepository;
import org.libreccm.core.Subject;
import org.libreccm.core.User;
import org.libreccm.core.UserRepository;
/**
* This pane contains three main segmented panel which only one is visible at
* any given time. The first panel is a table listing all available users in the
* system. The second panel displays read only user information. And the third
* panel is edit form.
*
* @author David Dao
* @author Ron Henderson
* @version $Id$
*/
class UserBrowsePane extends SegmentedPanel
implements TableCellRenderer,
TableActionListener,
Resettable,
ActionListener,
ChangeListener {
private static final Logger s_log = Logger.getLogger(UserBrowsePane.class);
private Component m_userBrowsePanel;
private Component m_userInfoPanel;
private Component m_userEditPanel;
private Component m_userPasswordPanel;
private Component m_groupMembershipPanel;
private Component m_actionPanel;
private Component m_extremeActionPanel;
private Component m_userDeleteFailedPanel;
private GroupAdministrationTab m_groupAdministrationTab;
private TabbedPane m_tabbedPane;
private List m_groupList = null;
private ArrayList m_panelList = new ArrayList();
private RequestLocal m_user;
@Override
public void register(Page p) {
for (int i = 0; i < m_panelList.size(); i++) {
p.setVisibleDefault((Component) m_panelList.get(i), false);
}
p.setVisibleDefault(m_userBrowsePanel, true);
p.addActionListener(this);
}
@Override
public void actionPerformed(final ActionEvent event) {
final PageState state = event.getPageState();
final CdiUtil cdiUtil = new CdiUtil();
final CcmSessionContext sessionContext;
try {
sessionContext = cdiUtil.findBean(CcmSessionContext.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final Subject subject = sessionContext.getCurrentSubject();
final Long userID = (Long) state.getValue(USER_ID_PARAM);
// Bug #167607 remove link for current user
if (m_userInfoPanel.isVisible(state)) {
if (subject.getSubjectId() == userID) {
m_extremeActionPanel.setVisible(state, false);
} else {
m_extremeActionPanel.setVisible(state, true);
}
}
}
/**
* Creates a new UserBrowsePane with multiple panels to help manage various
* aspects of a user's account.
*/
public UserBrowsePane() {
m_user = new RequestLocal() {
@Override
protected Object initialValue(final PageState state) {
final Long id = (Long) state.getValue(USER_ID_PARAM);
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final User user = userRepository.findById(id);
if (user == null) {
throw new UncheckedWrapperException(String.format(
"Failed to retrieve user: %d", id));
}
return user;
}
};
m_userBrowsePanel = buildUserBrowsePanel();
m_panelList.add(m_userBrowsePanel);
m_userInfoPanel = buildUserInfoPanel();
m_panelList.add(m_userInfoPanel);
m_userEditPanel = buildUserEditPanel();
m_panelList.add(m_userEditPanel);
m_userPasswordPanel = buildUserPasswordPanel();
m_panelList.add(m_userPasswordPanel);
m_groupMembershipPanel = buildGroupMembershipPanel();
m_panelList.add(m_groupMembershipPanel);
m_actionPanel = buildActionPanel();
m_panelList.add(m_actionPanel);
m_extremeActionPanel = buildExtremeActionPanel();
m_panelList.add(m_extremeActionPanel);
m_userDeleteFailedPanel = buildUserDeleteFailedPanel();
m_panelList.add(m_userDeleteFailedPanel);
}
/**
* Get user object for this request.
*/
public User getUser(PageState ps) {
return (User) m_user.get(ps);
}
/**
* Build the User Information panel
*/
private Component buildUserInfoPanel() {
// Edit user link
ActionLink link = new ActionLink(new Label(new GlobalizedMessage(
"ui.admin.user.editlink",
BUNDLE_NAME)));
link.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PageState ps = e.getPageState();
displayEditPanel(ps);
}
});
link.setClassAttr("actionLink");
BoxPanel panel = new BoxPanel();
// panel.add(new UserInfo(this));
final ColumnPanel colPanel = new ColumnPanel(2);
colPanel.add(new Label(new GlobalizedMessage(
"ui.admin.user.userinfo.name", BUNDLE_NAME)));
final Label userName = new Label();
userName.addPrintListener(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
final Label target = (Label) event.getTarget();
final PageState state = event.getPageState();
final User user = getUser(state);
target.setLabel(user.getScreenName());
}
});
colPanel.add(userName);
colPanel.add(new Label(new GlobalizedMessage(
"ui.admin.user.userinfo.screenname",
BUNDLE_NAME)));
final Label userScreenname = new Label();
userScreenname.addPrintListener(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
final Label target = (Label) event.getTarget();
final PageState state = event.getPageState();
final User user = getUser(state);
target.setLabel(user.getScreenName());
}
});
colPanel.add(userScreenname);
colPanel.add(new Label(new GlobalizedMessage(
"ui.admin.user.userinfo.primaryemail",
BUNDLE_NAME)));
final Label userEmail = new Label();
userEmail.addPrintListener(new PrintListener() {
@Override
public void prepare(final PrintEvent event) {
final Label target = (Label) event.getTarget();
final PageState state = event.getPageState();
final User user = getUser(state);
target.setLabel(user.getEmailAddresses().get(0).getAddress());
}
});
colPanel.add(userEmail);
panel.add(colPanel);
panel.add(link);
return addSegment(USER_INFO_LABEL, panel);
}
/**
* Build the User Edit panel
*/
private Component buildUserEditPanel() {
return addSegment(USER_EDIT_PANEL_HEADER, new UserEditForm(this));
}
/**
* Build the User Password Update panel
*/
private Component buildUserPasswordPanel() {
BoxPanel p = new BoxPanel();
p.add(new UserPasswordForm(this));
p.add(new SimpleContainer("admin:PasswordNote", ADMIN_XML_NS));
return addSegment(USER_PASSWORD_PANEL_HEADER, p);
}
/**
* Build the Group Membership panel
*/
private Component buildGroupMembershipPanel() {
m_groupList = new List();
m_groupList.setClassAttr("UserGroupsResultList");
m_groupList.setModelBuilder(new GroupsModelBuilder());
m_groupList.addChangeListener(this);
return addSegment(USER_GROUP_PANEL_HEADER, m_groupList);
}
/**
* Build the Action panel
*/
private Component buildActionPanel() {
BoxPanel p = new BoxPanel();
// Update password link
ActionLink link = new ActionLink(UPDATE_USER_PASSWORD_LABEL);
link.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PageState ps = e.getPageState();
displayUserPasswordPanel(ps);
}
});
link.setClassAttr("actionLink");
p.add(link);
// Become user link
// This will not be shown when the user is banned to prevent security issues
link = new ActionLink(BECOME_USER_LABEL) {
public boolean isVisible(PageState s) {
if (!super.isVisible(s)) {
return false;
}
User u = getUser(s);
return (!u.isBanned());
}
};
link.setClassAttr("actionLink");
link.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
final PageState state = event.getPageState();
// final BigDecimal id = (BigDecimal) state.getValue(USER_ID_PARAM);
//
// final CdiUtil cdiUtil = new CdiUtil();
// final LoginManager loginManager;
// try {
// loginManager = cdiUtil.findBean(
// LoginManager.class);
// } catch(CdiLookupException ex) {
// throw new UncheckedWrapperException(ex);
// }
//
// loginManager.login(CLASS, CLASS);
//
// try {
// UserContext uc = Web.getUserContext();
// uc.login(id);
// } catch (javax.security.auth.login.LoginException ex) {
// throw new UncheckedWrapperException("access denied", ex);
// }
// Redirect to workspace URL
final String path = UI.getUserRedirectURL(state.getRequest());
final URL url = URL.there(state.getRequest(), path);
throw new RedirectSignal(url, true);
}
});
p.add(link);
// Show all users
link = new ActionLink(new Label(new GlobalizedMessage(
"ui.admin.user.browselink",
BUNDLE_NAME)));
link.setClassAttr("actionLink");
link.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PageState ps = e.getPageState();
displayUserBrowsePanel(ps);
}
});
p.add(link);
return addSegment(USER_ACTION_PANEL_HEADER, p);
}
/**
* Build the Extreme Action panel
*/
private Component buildExtremeActionPanel() {
ActionLink deleteLink = new ActionLink(USER_DELETE_LABEL) {
@Override
public boolean isVisible(PageState s) {
if (!super.isVisible(s)) {
return false;
}
// We show the delete link if the user has never published an item
// This implicitly checks whether the user is banned - if they
// are deletable they cannot ever have been banned
User u = getUser(s);
return false;
}
};
deleteLink.setClassAttr("actionLink");
deleteLink.setConfirmation(USER_DELETE_CONFIRMATION.localize()
.toString());
deleteLink.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
final PageState state = event.getPageState();
final User user = getUser(state);
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
userRepository.delete(user);
displayUserBrowsePanel(state);
} // End ActionPerformed method
} // End of new ActionListener definition
);
// Add link inside a BoxPanel for correct alignment with other
// page elements.
ActionLink banLink = new ActionLink(USER_BAN_LABEL) {
@Override
public boolean isVisible(PageState s) {
if (!super.isVisible(s)) {
return false;
}
// We show the ban link if the user is not already banned and has never published
// an item
User u = getUser(s);
return (!u.isBanned());
}
};
banLink.setClassAttr("actionLink");
banLink.setConfirmation(USER_BAN_CONFIRMATION.localize().toString());
banLink.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PageState state = e.getPageState();
User user = getUser(state);
user.setBanned(true);
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
userRepository.save(user);
} // End ActionPerformed method
} // End of new ActionListener definition
);
ActionLink unbanLink = new ActionLink(USER_UNBAN_LABEL) {
public boolean isVisible(PageState s) {
if (!super.isVisible(s)) {
return false;
}
PageState state = s.getPageState();
User user = getUser(state);
return user.isBanned();
}
};
unbanLink.setClassAttr("actionLink");
unbanLink.setConfirmation(USER_UNBAN_CONFIRMATION.localize().toString());
unbanLink.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PageState state = e.getPageState();
User user = getUser(state);
user.setBanned(false);
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
userRepository.save(user);
} // End ActionPerformed method
} // End of new ActionListener definition
);
// Add link inside a BoxPanel for correct alignment with other
// page elements.
BoxPanel p = new BoxPanel();
p.add(deleteLink);
p.add(banLink);
p.add(unbanLink);
return addSegment(USER_TAB_EXTREME_ACTION_LABEL, p);
}
/**
* Build a panel to display an error message when unable to delete a user.
*/
private Component buildUserDeleteFailedPanel() {
ActionLink link = new ActionLink(USER_ACTION_CONTINUE);
link.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PageState state = e.getPageState();
displayUserInfoPanel(state);
}
});
Label label = new Label(USER_DELETE_FAILED_MSG);
label.setClassAttr("deleteFailedMessage");
BoxPanel p = new BoxPanel();
p.add(label);
p.add(link);
return addSegment(USER_DELETE_FAILED_PANEL_HEADER, p);
}
/**
* Build the Browse User panel. Displays a list of all registered users.
*/
private Component buildUserBrowsePanel() {
String headers[] = new String[]{
"ID", "Name", "Screen Name", "Email", "SSO login"
};
Table table = new Table(new UserTableModelBuilder(), headers);
table.setClassAttr("AlternateTable");
table.setDefaultCellRenderer(this);
table.addTableActionListener(this);
return addSegment(BROWSE_USER_PANEL_HEADER, table);
}
private class GroupsModelBuilder extends LockableImpl
implements ListModelBuilder {
@Override
public ListModel makeModel(final List list, final PageState state) {
final User user = getUser(state);
final java.util.List<GroupMembership> memberships = user
.getGroupMemberships();
final java.util.List<Subject> groups = new ArrayList<>();
for (GroupMembership membership : memberships) {
groups.add(membership.getGroup());
}
return new PartyListModel(groups);
}
}
void displayUserInfoPanel(PageState ps) {
hideAll(ps);
m_userInfoPanel.setVisible(ps, true);
m_groupMembershipPanel.setVisible(ps, true);
m_actionPanel.setVisible(ps, true);
m_extremeActionPanel.setVisible(ps, true);
}
void displayEditPanel(PageState ps) {
hideAll(ps);
m_userEditPanel.setVisible(ps, true);
}
public void displayUserBrowsePanel(PageState ps) {
hideAll(ps);
m_userBrowsePanel.setVisible(ps, true);
}
public void displayUserPasswordPanel(PageState ps) {
hideAll(ps);
m_userPasswordPanel.setVisible(ps, true);
}
public void displayUserDeleteFailedPanel(PageState ps) {
hideAll(ps);
m_userDeleteFailedPanel.setVisible(ps, true);
}
/**
* Hides all components of the UserBrowsePane in preparation for turning
* selected components back on.
*/
private void hideAll(PageState ps) {
for (int i = 0; i < m_panelList.size(); i++) {
((Component) m_panelList.get(i)).setVisible(ps, false);
}
}
@Override
public Component getComponent(Table table, PageState state, Object value,
boolean isSelected, Object key, int row,
int col) {
if (col == 0) {
ControlLink link = new ControlLink(value.toString());
return link;
} else {
if (value != null) {
return new Label(value.toString());
} else {
return new Label("&nbsp;", false);
}
}
}
@Override
public void cellSelected(TableActionEvent e) {
PageState ps = e.getPageState();
ps.setValue(USER_ID_PARAM, new BigDecimal((String) e.getRowKey()));
displayUserInfoPanel(ps);
}
@Override
public void headSelected(TableActionEvent e) {
// Empty
}
@Override
public void reset(PageState ps) {
displayUserBrowsePanel(ps);
}
public void setTabbedPane(TabbedPane tabbedPane) {
m_tabbedPane = tabbedPane;
}
public void setGroupAdministrationTab(
GroupAdministrationTab groupAdministrationTab) {
m_groupAdministrationTab = groupAdministrationTab;
}
// // This is how we check if a user is banned or not
// private boolean hasUserPublishedItems(User user) {
// DataQuery query = user.getSession().retrieveQuery(
// "com.arsdigita.versioning.UserPublications");
// query.setParameter("value", new Integer(user.getID().intValue()));
// query.next();
// Integer count = (Integer) query.get("theCount");
// query.close();
// return count.intValue() != 0;
// }
/**
* Display group information panel when a group name is clicked.
*/
@Override
public void stateChanged(final ChangeEvent event) {
if (event.getSource() == m_groupList) {
if (m_tabbedPane != null && m_groupAdministrationTab != null) {
PageState ps = event.getPageState();
String id = (String) m_groupList.getSelectedKey(ps);
if (id != null) {
final CdiUtil cdiUtil = new CdiUtil();
final GroupRepository groupRepository;
try {
groupRepository = cdiUtil
.findBean(GroupRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final Group group = groupRepository.findById(Long.parseLong(
id));
m_groupAdministrationTab.setGroup(ps, group);
m_groupAdministrationTab.displayGroupInfoPanel(ps);
m_tabbedPane.setSelectedIndex(ps, GROUP_TAB_INDEX);
} else {
reset(ps);
}
}
}
}
}
class UserTableModelBuilder extends LockableImpl implements TableModelBuilder {
public TableModel makeModel(Table t, PageState s) {
return new UserTableModel();
}
}
class UserTableModel implements TableModel {
private final java.util.List<User> users;
private int index = 0;
public UserTableModel() {
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
users = userRepository.findAll();
}
@Override
public int getColumnCount() {
return 5;
}
@Override
public Object getElementAt(final int columnIndex) {
final User user = users.get(index);
if (columnIndex == 0) {
return user.getSubjectId();
} else if (columnIndex == 1) {
return String.format("%s %s",
user.getName().getGivenName(),
user.getName().getFamilyName());
} else if (columnIndex == 2) {
return user.getScreenName();
} else if (columnIndex == 3) {
return user.getEmailAddresses().get(0).getAddress();
} else if (columnIndex == 4) {
return user.getSsoLogin();
} else {
return null;
}
}
@Override
public Object getKeyAt(final int columnIndex) {
return users.get(index).getSubjectId();
}
@Override
public boolean nextRow() {
index++;
return index < users.size();
}
}

View File

@ -0,0 +1,144 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.event.FormInitListener;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.util.UncheckedWrapperException;
import static com.arsdigita.ui.admin.AdminConstants.*;
import javax.mail.internet.InternetAddress;
import org.apache.log4j.Logger;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.EmailAddress;
import org.libreccm.core.PersonName;
import org.libreccm.core.User;
import org.libreccm.core.UserRepository;
import java.io.UncheckedIOException;
/**
* Form used to edit the information for a user.
*
* @version $Id$
*/
class UserEditForm extends UserForm
implements FormInitListener,
FormProcessListener {
// Logging
private static final Logger s_log = Logger.getLogger(UserEditForm.class);
private UserBrowsePane m_browsePane;
/**
* Constructor
*/
public UserEditForm(UserBrowsePane browsePane) {
super(USER_FORM_EDIT);
m_browsePane = browsePane;
addInitListener(this);
addProcessListener(this);
}
/**
* Initialize the form
*/
public void init(FormSectionEvent e) {
PageState state = e.getPageState();
hideSecurityInfo(state);
final User user = m_browsePane.getUser(state);
final PersonName name = user.getName();
m_firstName.setValue(state, name.getGivenName());
m_lastName.setValue(state, name.getFamilyName());
m_primaryEmail.setValue(state, user.getEmailAddresses().get(
0).getAddress());
m_screenName.setValue(state, user.getScreenName());
USER_FORM_LABEL_ADDITIONAL_EMAIL_LIST.setVisible(state, true);
m_emailList.setVisible(state, true);
}
/**
* Process the form
*/
@Override
public void process(final FormSectionEvent event)
throws FormProcessException {
final PageState state = event.getPageState();
final User user = m_browsePane.getUser(state);
final PersonName name = user.getName();
name.setGivenName((String) m_firstName.getValue(state));
name.setFamilyName((String) m_lastName.getValue(state));
user.setScreenName((String) m_screenName.getValue(state));
InternetAddress additional = (InternetAddress) m_additionalEmail
.getValue(state);
if (additional != null) {
final EmailAddress additionalEmail = new EmailAddress();
additional.setAddress(additional.getAddress());
user.addEmailAddress(additionalEmail);
}
// Check to see if the primary email address has changed, and
// if so set it to the new value and delete the association
// with the old. If it hasn't change don't do anything.
final EmailAddress oaddr = user.getEmailAddresses().get(0);
final EmailAddress naddr = new EmailAddress();
naddr.setAddress((String) m_primaryEmail.getValue(state));
if (!oaddr.equals(naddr)) {
if (s_log.isDebugEnabled()) {
s_log.debug("Changing primary email " + oaddr + " to " + naddr);
}
user.addEmailAddress(naddr);
user.removeEmailAddress(oaddr);
}
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
userRepository.save(user);
m_browsePane.displayUserInfoPanel(state);
}
}

View File

@ -0,0 +1,311 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.FormValidationListener;
import com.arsdigita.bebop.event.ParameterEvent;
import com.arsdigita.bebop.form.Password;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.EmailParameter;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.StringLengthValidationListener;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.parameters.URLParameter;
import com.arsdigita.kernel.KernelConfig;
import com.arsdigita.kernel.security.SecurityConfig;
import static com.arsdigita.ui.admin.AdminConstants.*;
import com.arsdigita.ui.login.PasswordValidationListener;
import com.arsdigita.util.StringUtils;
import com.arsdigita.util.UncheckedWrapperException;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.User;
import org.libreccm.core.UserRepository;
import java.io.UncheckedIOException;
import java.math.BigDecimal;
import javax.mail.internet.InternetAddress;
import javax.servlet.http.HttpServletRequest;
/**
* Generic form for adding and editting user.
*
* @author David Dao
* @version $Id$
*/
class UserForm extends Form implements FormValidationListener, AdminConstants {
protected TextField m_firstName;
protected TextField m_lastName;
protected TextField m_primaryEmail;
protected TextField m_additionalEmail;
protected Password m_password;
protected Password m_confirmPassword;
protected TextField m_question;
protected TextField m_answer;
protected TextField m_screenName;
protected EmailList m_emailList;
private final PasswordValidationListener m_pwListener;
private final NotEmptyValidationListener m_notNullListener;
private final SecurityConfig securityConfig = SecurityConfig.getConfig();
public UserForm(String formName) {
super(formName);
m_pwListener = new PasswordValidationListener();
m_notNullListener = new NotEmptyValidationListener();
addValidationListener(this);
// Bug #163373 add length checking for first/last names. We
// do this with both maximum length parameters in the user/add
// form and with validation of the value that come in for
// processing.
int max = 60;
m_firstName = new TextField(
new StringParameter(USER_FORM_INPUT_FIRST_NAME));
m_firstName.setMaxLength(max);
m_firstName.setSize(20);
m_firstName.addValidationListener(new NotEmptyValidationListener());
m_firstName.addValidationListener(
new StringLengthValidationListener(max));
add(USER_FORM_LABEL_FIRST_NAME);
add(m_firstName);
m_lastName = new TextField(
new StringParameter(USER_FORM_INPUT_LAST_NAME));
m_lastName.setMaxLength(max);
m_lastName.setSize(25);
m_lastName.addValidationListener(new NotEmptyValidationListener());
m_lastName
.addValidationListener(new StringLengthValidationListener(max));
add(USER_FORM_LABEL_LAST_NAME);
add(m_lastName);
// Password
m_password = new Password(new StringParameter(USER_FORM_INPUT_PASSWORD));
add(USER_FORM_LABEL_PASSWORD);
add(m_password);
// Password confirmation
m_confirmPassword = new Password(new StringParameter(
USER_FORM_INPUT_PASSWORD_CONFIRMATION));
add(USER_FORM_LABEL_PASSWORD_CONFIRMATION);
add(m_confirmPassword);
// Password question
m_question
= new TextField(new StringParameter(USER_FORM_INPUT_QUESTION));
m_question.setSize(50);
if (securityConfig.getEnableQuestion()) {
add(USER_FORM_LABEL_QUESTION);
add(m_question);
}
// Password answer
m_answer = new TextField(new StringParameter(USER_FORM_INPUT_ANSWER));
m_answer.setSize(50);
if (securityConfig.getEnableQuestion()) {
add(USER_FORM_LABEL_ANSWER);
add(m_answer);
}
// Primary email address
m_primaryEmail = new TextField(new EmailParameter(
USER_FORM_INPUT_PRIMARY_EMAIL));
m_primaryEmail.addValidationListener(new NotEmptyValidationListener());
m_primaryEmail.setSize(50);
add(USER_FORM_LABEL_PRIMARY_EMAIL);
add(m_primaryEmail);
// Additional email addresses
m_emailList = new EmailList();
add(USER_FORM_LABEL_ADDITIONAL_EMAIL_LIST);
add(m_emailList);
m_additionalEmail = new TextField(new EmailParameter(
USER_FORM_INPUT_ADDITIONAL_EMAIL));
m_additionalEmail.setSize(50);
add(USER_FORM_LABEL_ADDITIONAL_EMAIL);
add(m_additionalEmail);
// Screen name
m_screenName = new TextField(new StringParameter(
USER_FORM_INPUT_SCREEN_NAME));
if (KernelConfig.getConfig().screenNameIsPrimaryIdentifier()) {
m_screenName.addValidationListener(new NotEmptyValidationListener());
}
add(USER_FORM_LABEL_SCREEN_NAME);
add(m_screenName);
// Submit
add(new Label(""));
add(new Submit(USER_FORM_SUBMIT));
}
/**
* Validate the form. Verifies that the password and password-confirm fields
* match. If not it adds an error to the password-confirm field. Also
* verifies that primary email address and screen name are unique amoung all
* users.
*/
@Override
public void validate(FormSectionEvent event)
throws FormProcessException {
PageState ps = event.getPageState();
FormData data = event.getFormData();
HttpServletRequest req = ps.getRequest();
// UserID will be null if this is an add form.
BigDecimal userID = (BigDecimal) ps.getValue(USER_ID_PARAM);
/**
* Verify that password and confirmation match.
*/
if (userID == null) {
m_pwListener.validate(
new ParameterEvent(event.getSource(),
data.getParameter(
USER_FORM_INPUT_PASSWORD)));
m_notNullListener.validate(
new ParameterEvent(event.getSource(),
data.getParameter(
USER_FORM_INPUT_PASSWORD_CONFIRMATION)));
String password = (String) m_password.getValue(ps);
String confirm = (String) m_confirmPassword.getValue(ps);
if (!StringUtils.emptyString(password) && !StringUtils.emptyString(
confirm)) {
if (!password.equals(confirm)) {
data.addError(USER_FORM_INPUT_PASSWORD_CONFIRMATION,
(String) USER_FORM_ERROR_PASSWORD_NOT_MATCH.
localize(req));
}
}
}
if (securityConfig.getEnableQuestion()) {
// If the password answer is anything but null, make sure it
// contains some non-whitespace characters
String answer = (String) m_answer.getValue(ps);
if (userID == null) {
// Check for add form.
if (answer == null || answer.trim().length() == 0) {
data.addError(USER_FORM_INPUT_ANSWER,
(String) USER_FORM_ERROR_ANSWER_NULL.localize(
req));
}
} else {
// Check for edit form
if (answer != null && answer.length() > 0 && answer.trim().
length()
== 0) {
data.addError(USER_FORM_INPUT_ANSWER,
(String) USER_FORM_ERROR_ANSWER_NULL.localize(
req));
}
}
}
/**
* Verify that primary email and screen name are unique
*/
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final String screenName = (String) m_screenName.getValue(ps);
final User userByScreenname = userRepository.findByScreenName(
screenName);
final String email;
if (m_primaryEmail.getValue(ps) != null) {
email = ((InternetAddress) m_primaryEmail.getValue(ps)).getAddress();
} else {
email = null;
}
final User userByEmail = userRepository.findByEmailAddress(email);
if (userByScreenname != null && screenName != null && screenName.equals(
userByScreenname.getScreenName())) {
data.addError(USER_FORM_INPUT_SCREEN_NAME,
USER_FORM_ERROR_SCREEN_NAME_NOT_UNIQUE);
}
if (userByEmail != null
&& email != null
&& email.equals(userByEmail.getEmailAddresses().get(0).getAddress())) {
data.addError(USER_FORM_INPUT_PRIMARY_EMAIL,
USER_FORM_ERROR_PRIMARY_EMAIL_NOT_UNIQUE);
}
}
/**
* Hide all security-related components
*/
protected void hideSecurityInfo(PageState state) {
setSecurityInfo(state, false);
}
/**
* Show all security-related components
*/
protected void showSecurityInfo(PageState state) {
setSecurityInfo(state, true);
}
private void setSecurityInfo(PageState state, boolean isVisible) {
USER_FORM_LABEL_PASSWORD.setVisible(state, isVisible);
USER_FORM_LABEL_PASSWORD_CONFIRMATION.setVisible(state, isVisible);
if (securityConfig.getEnableQuestion()) {
USER_FORM_LABEL_QUESTION.setVisible(state, isVisible);
USER_FORM_LABEL_ANSWER.setVisible(state, isVisible);
}
m_password.setVisible(state, isVisible);
m_confirmPassword.setVisible(state, isVisible);
if (securityConfig.getEnableQuestion()) {
m_question.setVisible(state, isVisible);
m_answer.setVisible(state, isVisible);
}
}
}

View File

@ -0,0 +1,286 @@
/*
* Copyright (C) 2001-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.ui.admin;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
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.Password;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.NotNullValidationListener;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.mail.Mail;
import static com.arsdigita.ui.admin.AdminConstants.*;
import com.arsdigita.ui.login.PasswordValidationListener;
import com.arsdigita.util.UncheckedWrapperException;
import java.math.BigDecimal;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.libreccm.cdi.utils.CdiLookupException;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.User;
import org.libreccm.core.UserManager;
import org.libreccm.core.UserRepository;
/**
* Form used to update a user's password. It just provides form elements to
* enter the new password and confirm it. If the user doesn't have an
* authentication record, it will be created as part of setting the new value of
* the password.
*
* @version $Id$
*/
class UserPasswordForm extends Form
implements FormInitListener,
FormProcessListener,
FormValidationListener {
// Logging
private static final Logger s_log = Logger.getLogger(UserPasswordForm.class
.getName());
// Constants
final static String PASSWORD_FORM_NAME = "password-update";
final static String NEW_PASSWORD_PARAM_NAME = "password-new";
final static String CONFIRM_PASSWORD_PARAM_NAME = "password-confirm";
private UserBrowsePane m_userBrowsePane;
private TextField m_question;
private TextField m_answer;
private TextField m_ssoLogin;
/**
* Constructor.
*/
public UserPasswordForm(UserBrowsePane pane) {
super(PASSWORD_FORM_NAME);
m_userBrowsePane = pane;
setMethod(Form.POST);
addInitListener(this);
addValidationListener(this);
addProcessListener(this);
// Password
Password newPassword = new Password(NEW_PASSWORD_PARAM_NAME);
newPassword.addValidationListener(new PasswordValidationListener());
add(PASSWORD_FORM_LABEL_PASSWORD);
add(newPassword);
// Password confirmation
Password confirmPassword = new Password(CONFIRM_PASSWORD_PARAM_NAME);
confirmPassword.addValidationListener(new NotNullValidationListener());
add(PASSWORD_FORM_LABEL_CONFIRMATION_PASSWORD);
add(confirmPassword);
// Password question
m_question
= new TextField(new StringParameter(USER_FORM_INPUT_QUESTION));
m_question.setSize(50);
m_question.addValidationListener(new NotEmptyValidationListener());
add(PASSWORD_FORM_LABEL_QUESTION);
add(m_question);
// Password answer
m_answer = new TextField(new StringParameter(USER_FORM_INPUT_ANSWER));
m_answer.setSize(50);
add(PASSWORD_FORM_LABEL_ANSWER);
add(m_answer);
m_ssoLogin = new TextField(new StringParameter(USER_FORM_INPUT_SSO));
m_ssoLogin.setSize(50);
add(USER_FORM_LABEL_SSO);
add(m_ssoLogin);
// submit button
add(new Label(""));
add(new Submit(PASSWORD_FORM_SUBMIT));
}
/**
* Initialize the form
*/
@Override
public void init(final FormSectionEvent event) {
final PageState state = event.getPageState();
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final User user = userRepository.findById((Long) state.getValue(
USER_ID_PARAM));
m_question.setValue(state, user.getPasswordQuestion());
m_ssoLogin.setValue(state, user.getSsoLogin());
m_answer.setValue(state, "");
}
/**
* Validate the form input.
*/
@Override
public void validate(final FormSectionEvent event)
throws FormProcessException {
PageState state = event.getPageState();
FormData data = event.getFormData();
HttpServletRequest req = state.getRequest();
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final User user = userRepository.findById((Long) state.getValue(
USER_ID_PARAM));
if (user == null) {
return;
}
try {
// get parameter values
String newPassword = (String) data.get(NEW_PASSWORD_PARAM_NAME);
String confirmPassword = (String) data.get(
CONFIRM_PASSWORD_PARAM_NAME);
// check new password
if (!newPassword.equals(confirmPassword)) {
data.addError(CONFIRM_PASSWORD_PARAM_NAME,
(String) USER_FORM_ERROR_PASSWORD_NOT_MATCH
.localize(req));
return;
}
} finally {
if (!data.isValid()) {
// clear passwords from form data
data.put(NEW_PASSWORD_PARAM_NAME, "");
data.put(CONFIRM_PASSWORD_PARAM_NAME, "");
}
}
// If the password answer is anything but null, make sure it
// contains some non-whitespace characters
String answer = (String) m_answer.getValue(state);
if (answer != null && answer.length() > 0 && answer.trim().length() == 0) {
data.addError(USER_FORM_INPUT_ANSWER,
(String) USER_FORM_ERROR_ANSWER_NULL.localize(req));
}
}
/**
* Process the form
*/
public void process(FormSectionEvent event)
throws FormProcessException {
final PageState state = event.getPageState();
final FormData data = event.getFormData();
final CdiUtil cdiUtil = new CdiUtil();
final UserRepository userRepository;
final UserManager userManager;
try {
userRepository = cdiUtil.findBean(UserRepository.class);
userManager = cdiUtil.findBean(UserManager.class);
} catch(CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
final User user = userRepository.findById((Long) state.getValue(
USER_ID_PARAM));
if (user == null) {
throw new FormProcessException(GlobalizationUtil.globalize(
"ui.admin.user.userpasswordform.retrieving_user_failed"));
}
userManager.updatePassword(user, (String) data.get(NEW_PASSWORD_PARAM_NAME));
user.setPasswordQuestion((String) m_question.getValue(state));
final String answer = (String) m_answer.getValue(state);
if (answer != null && answer.length() > 0) {
user.setPasswordAnswer(answer);
}
user.setSsoLogin((String) m_ssoLogin.getValue(state));
userRepository.save(user);
BigDecimal id = (BigDecimal) state.getValue(USER_ID_PARAM);
s_log.debug("Committed password change");
notifyUser(user);
m_userBrowsePane.displayUserInfoPanel(state);
}
/**
* Notify user of the change to their password.
*
* TODO:
* <ol>
* <li>Message should be from the syadmin</li>
* <li>Globalize the subject and content</li>
* </ol>
*
* @param user is the User who's password was just changed.
*/
private void notifyUser(final User user) {
String to = user.getEmailAddresses().get(0).getAddress();
String from = to;
String subject = "Your password has been changed";
String nl = System.getProperty("line.separator");
StringBuffer sb = new StringBuffer();
sb.append("Dear ");
sb.append(user.getName().getGivenName());
sb.append(":");
sb.append(nl).append(nl);
sb.append("Your password has been changed by the ");
sb.append("system administrator.");
sb.append(nl);
try {
Mail.send(to, from, subject, sb.toString());
} catch (javax.mail.MessagingException e) {
s_log.error("Failed to notify user of password change");
}
}
}

View File

@ -18,7 +18,7 @@
*/
package org.libreccm.core;
import static org.libreccm.core.CoreConstants.*;
import static org.libreccm.core.CoreConstants.*;
import java.io.Serializable;
import java.util.ArrayList;
@ -42,14 +42,17 @@ import javax.xml.bind.annotation.XmlRootElement;
/**
* A {@code Group} is collection of {@link User}s.
*
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@Entity
@Table(name = "CCM_GROUPS", schema = DB_SCHEMA)
@NamedQueries({
@NamedQuery(name = "Group.findGroupByName",
query = "SELECT g FROM Group g WHERE g.name = :groupName")
@NamedQuery(name = "Group.findGroupByName",
query = "SELECT g FROM Group g WHERE g.name = :groupName"),
@NamedQuery(name = "Group.searchGroupByName",
query = "SELECT g FROM Group g "
+ "WHERE LOWER(g.name) LIKE '%:groupName%'")
})
@XmlRootElement(name = "user-group", namespace = CORE_XML_NS)
public class Group extends Subject implements Serializable {
@ -69,11 +72,11 @@ public class Group extends Subject implements Serializable {
*/
@OneToMany(mappedBy = "sourceGroup")
@XmlElementWrapper(name = "roles", namespace = CORE_XML_NS)
@XmlElement(name ="role", namespace = CORE_XML_NS)
@XmlElement(name = "role", namespace = CORE_XML_NS)
private List<Role> roles;
/**
* The members of the group. For adding or removing members the methods
* The members of the group. For adding or removing members the methods
* provided by the {@link GroupManager} should be used.
*/
@OneToMany(mappedBy = "group")
@ -160,7 +163,7 @@ public class Group extends Subject implements Serializable {
return false;
}
return Objects.equals(this.name, other.getName());
return Objects.equals(this.name, other.getName());
}
@Override

View File

@ -66,5 +66,13 @@ public class GroupRepository extends AbstractEntityRepository<Long, Group> {
return result.get(0);
}
}
public List<Group> searchGroupByName(final String groupName) {
final TypedQuery<Group> query = entityManager.createNamedQuery(
"Group.searchGroupByName", Group.class);
query.setParameter("groupName", groupName);
return query.getResultList();
}
}

View File

@ -0,0 +1,154 @@
ui.admin.dispatcher.accessDenied=Access Denied
ui.admin.dispatcher.contextTitle=Index
ui.admin.dispatcher.groupsLabel=Groups
ui.admin.dispatcher.title=Administration
ui.admin.dispatcher.usersLabel=Users
ui.admin.groups.actioncontinue=Continue
ui.admin.groups.groupdeletefailed=Unable to delete group
ui.admin.groups.add=Create new subgroup
ui.admin.groups.addeditform.namelabel=Name:
ui.admin.groups.addeditform.primaryemaillabel=Primary email:
ui.admin.groups.addeditform.submit=Save
ui.admin.groups.addgrouplabel=Add new group
ui.admin.groups.addsubmemberlabel=Add member
ui.admin.groups.delete=Delete group
ui.admin.groups.deletefailed=Error message
ui.admin.groups.edit=Edit
ui.admin.groups.groupedit=Edit Group
ui.admin.groups.extremeaction=Extreme Action
ui.admin.groups.groupinformation=Basic Information
ui.admin.groups.removesubmemberlabel=Remove
ui.admin.groups.subgroupcountlabel=Sub groups:
ui.admin.groups.subgroups=Subgroup Information
ui.admin.groups.submembers=Member Information
# new labels added - cg 15/6/04
ui.admin.groups.addExisting=Add existing group as subgroup
ui.admin.groups.removeExisting=Remove as subgroup
ui.admin.groups.search=Search for groups to add as subgroups
ui.admin.groups.button.search=Search
ui.admin.groups.searchForm.noResults=Your search matches no valid groups (groups that cause circular references are excluded). Search again
ui.admin.groups.found.title=Select groups
ui.admin.groups.select.explanation=Select the groups:
ui.admin.save=Save
ui.admin.nav.logout=Log out
ui.admin.nav.workspace=Workspace
ui.admin.searchAndList.submit=Search
ui.admin.searchAndList.submitAgain=Search Again
ui.admin.tab.group=Groups
ui.admin.tab.user.browse=Browse
ui.admin.tab.user.createuser=Create new user
ui.admin.tab.user.navbartitle=User Administration
ui.admin.tab.user.search=Search
ui.admin.tab.user.summary=Summary
ui.admin.tab.user=Users
ui.admin.user.action.continue=Continue
ui.admin.user.action.delete.failed.header=Unable to delete user
ui.admin.user.action.header=Actions
ui.admin.user.addeditform.additionalemail=Additional Email Address:
ui.admin.user.addeditform.additionalemaillist=Additional Email
ui.admin.user.addeditform.answer=Answer:
ui.admin.user.addeditform.confirmation=Password Confirmation:
ui.admin.user.addeditform.deleteemail=Delete
ui.admin.user.addeditform.error.answer.null=This parameter is required.
ui.admin.user.addeditform.error.answer.whitespace=Answer must be empty or contain non-whitespace characters
ui.admin.user.addeditform.error.password.notmatch=Passwords do not match
ui.admin.user.addeditform.error.primaryemail.notunique=Primary email is not unique
ui.admin.user.addeditform.error.screenname.notunique=Screen name is not unique
ui.admin.user.addeditform.firstname=First Name:
ui.admin.user.addeditform.lastname=Last Name:
ui.admin.user.addeditform.password=Password:
ui.admin.user.addeditform.primaryemail=Primary Email Address:
ui.admin.user.addeditform.question=Question:
ui.admin.user.addeditform.screenname=Screen Name:
ui.admin.user.addeditform.ssologinname=External Login:
ui.admin.user.addeditform.submit=Save
ui.admin.user.addeditform.url=URL:
ui.admin.user.browselink=Show All
ui.admin.user.browsepanel.becomeUser=Become this user
ui.admin.user.browsepanel.extremeaction=Extreme Actions
ui.admin.user.browsepanel.header=All Registered Users
ui.admin.user.browsepanel.updatePassword=Update Password
ui.admin.user.createpanel.header=Create new user
ui.admin.user.delete.confirm=Are you sure you want to delete this user?
ui.admin.user.ban.confirm=Are you sure you want to ban this user?
ui.admin.user.unban.confirm=Are you sure you want to unban this user?
ui.admin.user.ban.label=Ban user
ui.admin.user.unban.label=Unban user
ui.admin.user.delete.failed.label=User Delete Failed
ui.admin.user.delete.label=Delete user
ui.admin.user.editlink=Edit
ui.admin.user.groupmembership.header=Group Membership
ui.admin.user.password.header=Update Security Information
ui.admin.user.search.header=Search
ui.admin.user.summarypanel.createUser=Create new user
ui.admin.user.summarypanel.header=Summary
ui.admin.user.summarypanel.totalusers=Total users:
ui.admin.user.useredit.header=Edit User Information
ui.admin.user.userinfo.header=User Information
ui.admin.user.userpasswordform.answer=Answer:<br>(<font size="-1">Leave blank to retain current answer</font>)
ui.admin.user.userpasswordform.confirmpasswordlabel=Confirm password:
ui.admin.user.userpasswordform.passwordlabel=Password:
ui.admin.user.userpasswordform.question=Question:
ui.admin.user.userpasswordform.submit=Change
ui.admin.tab.applications=Applications
ui.admin.applications.tree.heading=Applications
ui.admin.applications.url.validation.not_blank=The URL of an application instance can is mandatory.
ui.admin.applications.url.valiation.minmaxlength=The length of an URL of an application instance must be between 1 and 100 characters.
ui.admin.applications.title.validation.not_blank=Title is mandatory for an application instance.
ui.admin.applications.title.valiation.minmaxlength=The minimum length of the title of an applicatio instance is one character, the maximum length are 200 characters
ui.admin.applications.desc.valiation.minmaxlength=The maximum length of a descrption of an application instance are 4000 characters.
ui.admin.applications.url.label=URL
ui.admin.applications.title.label=Title
ui.admin.applications.desc.label=Description
ui.admin.applications.url.validation.url_already_in_use=The provided URL is already in use
ui.admin.applications.url.validation.no_slash_allowed=The URL fragement may not contain slashes
ui.admin.applications.ApplicationInstancePane.title.label=Title of the instance
ui.admin.applications.ApplicationInstancePane.parent_app.label=Parent application
ui.admin.applications.ApplicationInstancePane.path.label=Path of the application
ui.admin.applications.ApplicationInstancePane.desc.label=Description
ui.admin.applications.ApplicationInstancePane.info.heading=Instance data
ui.admin.MultiInstanceApplicationPane.manage.heading=Edit instance specific settings
ui.admin.MultiInstancePane.manage.no_instance_admin_pane_found=No instance admin pane for instances of the application type '{0]' found. Maybe it is not implemented yet.
ui.admin.applications.ApplicationInfoSection.title.label=Title
ui.admin.applications.ApplicationInfoSection.app_class.label=Application Class
ui.admin.applications.ApplicationInfoSection.singleton.label=Singleton
ui.admin.applications.ApplicationInfoSection.singleton.yes=Yes
ui.admin.applications.ApplicationInfoSection.singleton.no=No
ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label=Path of the singleton instance
ui.admin.applications.ApplicationInfoSection.singleton_instance.no_instance_found=No instance found
ui.admin.applications.ApplicationInfoSection.heading=Application information
ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_title.header=Title
ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_url.header=Path
ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_desc.header=Description
ui.admin.MultiInstanceApplicationPane.instances=Instances
ui.admin.MultiInstanceApplicationPane.manage_instances.heading=Manage instances
ui.admin.MultiInstancePane.manage.no_create_form_found=No form for manageing instances of type '{0}' found.
ui.admin.MultiInstanceApplicationPane.create_instance=Create new instance
ui.admin.SingletonApplicationPane.manage.heading=Edit settings
ui.admin.SingletonApplicationPane.manage.no_admin_pane_found=No admin pane for applications of type '{0}' found.
ui.admin.applications.ApplicationInfoSection.desc.label=Description
ui.admin.applications.no_settings=This application has no settings (yet).
ui.admin.applications.form_not_compatible_now=This application administration form is not yet compatible with this application pane. Please use the applications own administration form.
ui.admin.applications.ApplicationInstancePane.manage.heading=Instance specific settings
ui.admin.applications.parent.label=Select parent application
ui.admin.user.userinfo.name=Name:
ui.admin.user.userinfo.screenname=Username:
ui.admin.user.userinfo.primaryemail=Email:
ui.admin.groups.name=Name:
ui.admin.applications.placeholder=There is no administration panel for instances of this application type yet.
ui.admin.cancel=Cancel
ui.admin.cancel_msg=Submission cancelled
ui.admin.tab.sysinfo.title=System information
ui.admin.sysinfo.appinfo=About
ui.admin.sysinfo.java_system_properties=Java system properties
ui.admin.sysinfo.xml_transformer_factory=XML Transformer Factory
ui.admin.sysinfo.xml_transformer=XML Transformer
ui.admin.sysinfo.xml_document_builder_factory=XML Document Builder Factory
ui.admin.sysinfo.xml_document_builder=XML Document Builder
ui.admin.sysinfo.sax_parser_factory=SAX Parser Factory
ui.admin.sysinfo.sax_parser=SAX Parser
ui.admin.sysinfo.xml_config=XML config
#ID ist null
ui.admin.groups.ID_is_null=ID is null
ui.admin.user.userpasswordform.retrieving_user_failed=Failed to retrieve user
ui.admin.groups.couldnt_find_specified_group=Couldn't find the specified group

View File

@ -0,0 +1,154 @@
ui.admin.dispatcher.accessDenied=Zugang abgelehnt
ui.admin.dispatcher.contextTitle=Index
ui.admin.dispatcher.groupsLabel=Gruppen
ui.admin.dispatcher.title=Administration
ui.admin.dispatcher.usersLabel=Benutzer
ui.admin.groups.actioncontinue=Fortsetzen
ui.admin.groups.groupdeletefailed=Gruppe nicht zu l\u00f6schbar
ui.admin.groups.add=Neue Untergruppe erstellen
ui.admin.groups.addeditform.namelabel=Name:
ui.admin.groups.addeditform.primaryemaillabel=Prim\u00e4re E-Mail\:
ui.admin.groups.addeditform.submit=Sichern
ui.admin.groups.addgrouplabel=Neue Gruppe hinzuf\u00fcgen
ui.admin.groups.addsubmemberlabel=Mitglied hinzuf\u00fcgen
ui.admin.groups.delete=Gruppe l\u00f6schen
ui.admin.groups.deletefailed=Fehlermeldung
ui.admin.groups.edit=Edit
ui.admin.groups.groupedit=Edit Group
ui.admin.groups.extremeaction=Extreme Action
ui.admin.groups.groupinformation=Basisinformationen
ui.admin.groups.removesubmemberlabel=Entfernen
ui.admin.groups.subgroupcountlabel=Untergruppen\:
ui.admin.groups.subgroups=Untergruppen Information
ui.admin.groups.submembers=Mitgliedsinformation
# new labels added - cg 15/6/04
ui.admin.groups.addExisting=Add existing group as subgroup
ui.admin.groups.removeExisting=Als Subgruppe entfernen
ui.admin.groups.search=Search for groups to add as subgroups
ui.admin.groups.button.search=Suche
ui.admin.groups.searchForm.noResults=Your search matches no valid groups (groups that cause circular references are excluded). Search again
ui.admin.groups.found.title=Gruppen ausw\u00e4hlen
ui.admin.groups.select.explanation=Gruppen ausw\u00e4hlen\:
ui.admin.save=Sichern
ui.admin.nav.logout=Abmelden
ui.admin.nav.workspace=Workspace
ui.admin.searchAndList.submit=Suchen
ui.admin.searchAndList.submitAgain=Erneut suchen
ui.admin.tab.group=Gruppen
ui.admin.tab.user.browse=Bl\u00e4ttern
ui.admin.tab.user.createuser=Neuen Benutzer erstellen
ui.admin.tab.user.navbartitle=Benutzerverwaltung
ui.admin.tab.user.search=Suche
ui.admin.tab.user.summary=Zusammenfassung
ui.admin.tab.user=Benutzer
ui.admin.user.action.continue=Fortfahren
ui.admin.user.action.delete.failed.header=Benutzer kann nicht gel\u00f6scht werden
ui.admin.user.action.header=Aktionen
ui.admin.user.addeditform.additionalemail=Weitere E-Mail Adresse\:
ui.admin.user.addeditform.additionalemaillist=Weitere E-Mail
ui.admin.user.addeditform.answer=Antwort\:
ui.admin.user.addeditform.confirmation=Password Confirmation:
ui.admin.user.addeditform.deleteemail=Entfernen
ui.admin.user.addeditform.error.answer.null=This parameter is required.
ui.admin.user.addeditform.error.answer.whitespace=Answer must be empty or contain non-whitespace characters
ui.admin.user.addeditform.error.password.notmatch=Passworte stimmen nicht \u00fcberein
ui.admin.user.addeditform.error.primaryemail.notunique=Primary email is not unique
ui.admin.user.addeditform.error.screenname.notunique=Benutzername ist nicht eindeutig
ui.admin.user.addeditform.firstname=Vorname\:
ui.admin.user.addeditform.lastname=Nachname\:
ui.admin.user.addeditform.password=Password:
ui.admin.user.addeditform.primaryemail=Primary Email Address:
ui.admin.user.addeditform.question=Frage\:
ui.admin.user.addeditform.screenname=Benutzername\:
ui.admin.user.addeditform.ssologinname=External Login:
ui.admin.user.addeditform.submit=Sichern
ui.admin.user.addeditform.url=URL:
ui.admin.user.browselink=Show All
ui.admin.user.browsepanel.becomeUser=Become this user
ui.admin.user.browsepanel.extremeaction=Extreme Actions
ui.admin.user.browsepanel.header=Alle registrierten Benutzer
ui.admin.user.browsepanel.updatePassword=Update Password
ui.admin.user.createpanel.header=Neuen Benutzer erstellen
ui.admin.user.delete.confirm=Are you sure you want to delete this user?
ui.admin.user.ban.confirm=Are you sure you want to ban this user?
ui.admin.user.unban.confirm=Are you sure you want to unban this user?
ui.admin.user.ban.label=Ban user
ui.admin.user.unban.label=Unban user
ui.admin.user.delete.failed.label=L\u00f6schen des Benutzers fehlgeschlagen
ui.admin.user.delete.label=Benutzer l\u00f6schen
ui.admin.user.editlink=Editieren
ui.admin.user.groupmembership.header=Group Membership
ui.admin.user.password.header=Update Security Information
ui.admin.user.search.header=Search
ui.admin.user.summarypanel.createUser=Create new user
ui.admin.user.summarypanel.header=Summary
ui.admin.user.summarypanel.totalusers=Total users:
ui.admin.user.useredit.header=Edit User Information
ui.admin.user.userinfo.header=Benutzerinformation
ui.admin.user.userpasswordform.answer=Antwort\:<br>(<font size\="-1">Leerlassen um aktuelle Antwort beizubehalten</font>)
ui.admin.user.userpasswordform.confirmpasswordlabel=Passwort best\u00e4tigen\:
ui.admin.user.userpasswordform.passwordlabel=Passwort\:
ui.admin.user.userpasswordform.question=Frage\:
ui.admin.user.userpasswordform.submit=\u00c4ndern
ui.admin.tab.applications=Applikationen
ui.admin.applications.tree.heading=Applikationen
ui.admin.applications.url.validation.not_blank=Die Angabe einer URL ist erforderlich
ui.admin.applications.url.valiation.minmaxlength=Die URL einer Applikations-Instanz muss zwischen einem und 100 Zeichen lang sein
ui.admin.applications.title.validation.not_blank=Der Titel ist f\u00fcr das Anlegen einer Applikations-Instanz erforderlich
ui.admin.applications.title.valiation.minmaxlength=Der Titel einer Applikations-Instanz muss zwischen einem und 200 Zeichen lang sein.
ui.admin.applications.desc.valiation.minmaxlength=Die Beschreibung einer Applikations-Instanz darf max. 4000 Zeichen lang sein
ui.admin.applications.url.label=URL
ui.admin.applications.title.label=Bezeichnung
ui.admin.applications.desc.label=Beschreibung
ui.admin.applications.url.validation.url_already_in_use=Der angegebene URL wird bereits verwendet
ui.admin.applications.url.validation.no_slash_allowed=Das URL-Fragment darf keine Schr\u00e4gstriche enthalten
ui.admin.applications.ApplicationInstancePane.title.label=Titel der Instanz
ui.admin.applications.ApplicationInstancePane.parent_app.label=\u00dcbergeordnete Applikation
ui.admin.applications.ApplicationInstancePane.path.label=Pfad der Applikation
ui.admin.applications.ApplicationInstancePane.desc.label=Beschreibung
ui.admin.applications.ApplicationInstancePane.info.heading=Daten der Instanz
ui.admin.MultiInstanceApplicationPane.manage.heading=Einstellungen der Instanz bearbeiten
ui.admin.MultiInstancePane.manage.no_instance_admin_pane_found=Kein Administrationsformular f\u00fcr Instanzen des Applikationstyps {0} gefunden. M\u00f6glicherweise noch nicht implementiert.
ui.admin.applications.ApplicationInfoSection.title.label=Titel
ui.admin.applications.ApplicationInfoSection.app_class.label=Applikationsklasse
ui.admin.applications.ApplicationInfoSection.singleton.label=Singleton
ui.admin.applications.ApplicationInfoSection.singleton.yes=Ja
ui.admin.applications.ApplicationInfoSection.singleton.no=Nein
ui.admin.applications.ApplicationInfoSection.singleton_instance.path.label=Pfad der Singleton Instanz
ui.admin.applications.ApplicationInfoSection.singleton_instance.no_instance_found=Keine Instanz gefunden
ui.admin.applications.ApplicationInfoSection.heading=Applikationsinformationen
ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_title.header=Titel
ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_url.header=Pfad
ui.admin.applicationsMultiInstanceApplicationPane.instances.table.col_desc.header=Beschreibung
ui.admin.MultiInstanceApplicationPane.instances=Instanzen
ui.admin.MultiInstanceApplicationPane.manage_instances.heading=Instanzen verwalten
ui.admin.MultiInstancePane.manage.no_create_form_found=Keine Formular zum verwalten von Applikationen des Types {0} gefunden.
ui.admin.MultiInstanceApplicationPane.create_instance=Neue instanz anlegen
ui.admin.SingletonApplicationPane.manage.heading=Eigenschaften bearbeiten
ui.admin.SingletonApplicationPane.manage.no_admin_pane_found=Keine Admin-Formular f\u00fcr Applikationen des Types {0} gefunden
ui.admin.applications.ApplicationInfoSection.desc.label=Beschreibung
ui.admin.applications.no_settings=Diese Applikation hat (noch) keine Einstellungen.
ui.admin.applications.form_not_compatible_now=Das Formular zur Verwaltung dieser Application ist derzeit noch nicht kompatibel mit dieser Administrationsoberfl\u00e4che. Bitte nutzen Sie den Administrationsoberfl\u00e4che der Application.
ui.admin.applications.ApplicationInstancePane.manage.heading=Einstellungen der Instanz
ui.admin.applications.parent.label=W\u00e4hlen Sie die \u00fcbergeordnete Applikation
ui.admin.user.userinfo.name=Name:
ui.admin.user.userinfo.screenname=Benutzername:
ui.admin.user.userinfo.primaryemail=E-Mail:
ui.admin.groups.name=Name:
ui.admin.applications.placeholder=F\u00fcr Applikationen dieses Typs gibt es noch keine Verwaltungsoberfl\u00e4che.
ui.admin.cancel=Abbrechen
ui.admin.cancel_msg=Bearbeitung abgebrochen
ui.admin.tab.sysinfo.title=Systeminformationen
ui.admin.sysinfo.appinfo=\u00dcber
ui.admin.sysinfo.java_system_properties=Java System Properties
ui.admin.sysinfo.xml_transformer_factory=XML Transformer Factory
ui.admin.sysinfo.xml_transformer=XML Transformer
ui.admin.sysinfo.xml_document_builder_factory=XML Document Builder Factory
ui.admin.sysinfo.xml_document_builder=XML Document Builder
ui.admin.sysinfo.sax_parser_factory=SAX Parser Factory
ui.admin.sysinfo.sax_parser=SAX Parser
ui.admin.sysinfo.xml_config=XML Konfiguration
#ID ist null
ui.admin.groups.ID_is_null=ID is null
ui.admin.user.userpasswordform.retrieving_user_failed=Konnte Benutzer nicht abrufen
ui.admin.groups.couldnt_find_specified_group=Konnte die spezifische Gruppe nicht finden

View File

@ -0,0 +1,112 @@
ui.admin.dispatcher.accessDenied=Access Denied
ui.admin.dispatcher.contextTitle=Index
ui.admin.dispatcher.groupsLabel=Groups
ui.admin.dispatcher.title=Administration
ui.admin.dispatcher.usersLabel=Users
ui.admin.groups.actioncontinue=Continue
ui.admin.groups.groupdeletefailed=Unable to delete group
ui.admin.groups.add=Create new subgroup
ui.admin.groups.addeditform.namelabel=Name:
ui.admin.groups.addeditform.primaryemaillabel=Primary email:
ui.admin.groups.addeditform.submit=Save
ui.admin.groups.addgrouplabel=Add new group
ui.admin.groups.addsubmemberlabel=Add member
ui.admin.groups.delete=Delete group
ui.admin.groups.deletefailed=Error message
ui.admin.groups.edit=Edit
ui.admin.groups.groupedit=Edit Group
ui.admin.groups.extremeaction=Extreme Action
ui.admin.groups.groupinformation=Basic Information
ui.admin.groups.removesubmemberlabel=Remove
ui.admin.groups.subgroupcountlabel=Sub groups:
ui.admin.groups.subgroups=Subgroup Information
ui.admin.groups.submembers=Member Information
# new labels added - cg 15/6/04
ui.admin.groups.addExisting=Add existing group as subgroup
ui.admin.groups.removeExisting=Remove as subgroup
ui.admin.groups.search=Search for groups to add as subgroups
ui.admin.groups.button.search=Search
ui.admin.groups.searchForm.noResults=Your search matches no valid groups (groups that cause circular references are excluded). Search again
ui.admin.groups.found.title=Select groups
ui.admin.groups.select.explanation=Select the groups:
ui.admin.save=Save
ui.admin.nav.logout=Log out
ui.admin.nav.workspace=Workspace
ui.admin.searchAndList.submit=Search
ui.admin.searchAndList.submitAgain=Search Again
ui.admin.tab.group=Groups
ui.admin.tab.user.browse=Browse
ui.admin.tab.user.createuser=Create new user
ui.admin.tab.user.navbartitle=User Administration
ui.admin.tab.user.search=Search
ui.admin.tab.user.summary=Summary
ui.admin.tab.user=Users
ui.admin.user.action.continue=Continue
ui.admin.user.action.delete.failed.header=Unable to delete user
ui.admin.user.action.header=Actions
ui.admin.user.addeditform.additionalemail=Additional Email Address:
ui.admin.user.addeditform.additionalemaillist=Additional Email
ui.admin.user.addeditform.answer=Answer:
ui.admin.user.addeditform.confirmation=Password Confirmation:
ui.admin.user.addeditform.deleteemail=Delete
ui.admin.user.addeditform.error.answer.null=This parameter is required.
ui.admin.user.addeditform.error.answer.whitespace=Answer must be empty or contain non-whitespace characters
ui.admin.user.addeditform.error.password.notmatch=Passwords do not match
ui.admin.user.addeditform.error.primaryemail.notunique=Primary email is not unique
ui.admin.user.addeditform.error.screenname.notunique=Screen name is not unique
ui.admin.user.addeditform.firstname=First Name:
ui.admin.user.addeditform.lastname=Last Name:
ui.admin.user.addeditform.password=Password:
ui.admin.user.addeditform.primaryemail=Primary Email Address:
ui.admin.user.addeditform.question=Question:
ui.admin.user.addeditform.screenname=Screen Name:
ui.admin.user.addeditform.ssologinname=External Login:
ui.admin.user.addeditform.submit=Save
ui.admin.user.addeditform.url=URL:
ui.admin.user.browselink=Show All
ui.admin.user.browsepanel.becomeUser=Become this user
ui.admin.user.browsepanel.extremeaction=Extreme Actions
ui.admin.user.browsepanel.header=All Registered Users
ui.admin.user.browsepanel.updatePassword=Update Password
ui.admin.user.createpanel.header=Create new user
ui.admin.user.delete.confirm=Are you sure you want to delete this user?
ui.admin.user.ban.confirm=Are you sure you want to ban this user?
ui.admin.user.unban.confirm=Are you sure you want to unban this user?
ui.admin.user.ban.label=Ban user
ui.admin.user.unban.label=Unban user
ui.admin.user.delete.failed.label=User Delete Failed
ui.admin.user.delete.label=Delete user
ui.admin.user.editlink=Edit
ui.admin.user.groupmembership.header=Group Membership
ui.admin.user.password.header=Update Security Information
ui.admin.user.search.header=Search
ui.admin.user.summarypanel.createUser=Create new user
ui.admin.user.summarypanel.header=Summary
ui.admin.user.summarypanel.totalusers=Total users:
ui.admin.user.useredit.header=Edit User Information
ui.admin.user.userinfo.header=User Information
ui.admin.user.userpasswordform.answer=Answer:<br>(<font size="-1">Leave blank to retain current answer</font>)
ui.admin.user.userpasswordform.confirmpasswordlabel=Confirm password:
ui.admin.user.userpasswordform.passwordlabel=Password:
ui.admin.user.userpasswordform.question=Question:
ui.admin.user.userpasswordform.submit=Change
ui.admin.tab.applications=Applications
ui.admin.applications.tree.heading=Applications
ui.admin.applications.url.validation.not_blank=The URL of an application instance can is mandatory.
ui.admin.applications.url.valiation.minmaxlength=The length of an URL of an application instance must be between 1 and 100 characters.
ui.admin.applications.title.validation.not_blank=Title is mandatory for an application instance.
ui.admin.applications.title.valiation.minmaxlength=The minimum length of the title of an applicatio instance is one character, the maximum length are 200 characters
ui.admin.applications.desc.valiation.minmaxlength=The maximum length of a descrption of an application instance are 4000 characters.
ui.admin.applications.url.label=URL
ui.admin.applications.title.label=Title
ui.admin.applications.desc.label=Description
ui.admin.applications.url.validation.url_already_in_use=The provided URL is already in use
ui.admin.applications.url.validation.no_slash_allowed=The URL fragement may not contain slashes
ui.admin.applications.ApplicationInstancePane.title.label=Title of the instance
ui.admin.applications.ApplicationInstancePane.parent_app.label=Parent application
ui.admin.cancel=Cancel
ui.admin.cancel_msg=Submission cancelled
#ID ist null
ui.admin.groups.ID_is_null=ID is null
ui.admin.user.userpasswordform.retrieving_user_failed=Failed to retrieve user
ui.admin.groups.couldnt_find_specified_group=Couldn't find the specified group

View File

@ -0,0 +1,97 @@
ui.admin.dispatcher.accessDenied=Acc\u00e8s refus\u00e9
ui.admin.dispatcher.contextTitle=Index
ui.admin.dispatcher.groupsLabel=Groupes
ui.admin.dispatcher.title=Administration
ui.admin.dispatcher.usersLabel=Utilisateurs
ui.admin.groups.actioncontinue=Continuer
ui.admin.groups.groupdeletefailed=Impossible de supprimer le groupe
ui.admin.groups.add=Ajouter un sous-groupe
ui.admin.groups.addeditform.namelabel=Nom:
ui.admin.groups.addeditform.primaryemaillabel=e-mail principal:
ui.admin.groups.addeditform.submit=Enregistrer
ui.admin.groups.addgrouplabel=Ajouter un nouveau group
ui.admin.groups.addsubmemberlabel=Ajouter un membre
ui.admin.groups.delete=Effacer un groupe
ui.admin.groups.deletefailed=Message d'erreur
ui.admin.groups.edit=Modifier
ui.admin.groups.groupedit=Modifier le groupe
ui.admin.groups.extremeaction=Action dangereuse
ui.admin.groups.groupinformation=Information de base
ui.admin.groups.removesubmemberlabel=Retirer
ui.admin.groups.subgroupcountlabel=Sous-groupes:
ui.admin.groups.subgroups=Information sur le sous-groupe
ui.admin.groups.submembers=Information sur le membre
ui.admin.nav.logout=D\u00e9connexion
ui.admin.nav.workspace=Espace de travail
ui.admin.searchAndList.submit=Rechercher
ui.admin.searchAndList.submitAgain=Rechercher suivant
ui.admin.tab.group=Groupe
ui.admin.tab.user.browse=Parcourir
ui.admin.tab.user.createuser=Cr\u00e9er un nouvel utilisateur
ui.admin.tab.user.navbartitle=Gestion de l'utilisateur
ui.admin.tab.user.search=Rechercher
ui.admin.tab.user.summary=Table des mati\u00e8res
ui.admin.tab.user=Utilisateurs
ui.admin.user.action.continue=Continuer
ui.admin.user.action.delete.failed.header=Impossible de supprimer l'utiisateur
ui.admin.user.action.header=Actions
ui.admin.user.addeditform.additionalemail=Adresse e-mail secondaire:
ui.admin.user.addeditform.additionalemaillist=Autre e-mail
ui.admin.user.addeditform.answer=R\u00e9ponse:
ui.admin.user.addeditform.confirmation=Confirmation du mot de passe:
ui.admin.user.addeditform.deleteemail=Supprimer
ui.admin.user.addeditform.error.answer.null=Ce param\u00e8tre est obligatoire.
ui.admin.user.addeditform.error.answer.whitespace=La r\u00e9ponse doit \u00eatre vide ou ne pas contenir d'espace
ui.admin.user.addeditform.error.password.notmatch=Les mots de passe sont diff\u00e9rents
ui.admin.user.addeditform.error.primaryemail.notunique=L'e-mail principal n'est pas unique
ui.admin.user.addeditform.error.screenname.notunique=Screen name n'est pas unique
ui.admin.user.addeditform.firstname=Pr\u00e9nom:
ui.admin.user.addeditform.lastname=Nom:
ui.admin.user.addeditform.password=Mot de passe:
ui.admin.user.addeditform.primaryemail=e-mail principal:
ui.admin.user.addeditform.question=Question:
ui.admin.user.addeditform.screenname=Screen Name:
ui.admin.user.addeditform.submit=Enregistrer
ui.admin.user.addeditform.url=URL:
ui.admin.user.browselink=Montrer tout
ui.admin.user.browsepanel.becomeUser=Devenir cet utilisateur
ui.admin.user.browsepanel.extremeaction=Action dangereuse
ui.admin.user.browsepanel.header=Tous les utilisateurs enregistr\u00e9s
ui.admin.user.browsepanel.updatePassword=Mettre \u00e0 jour le mot de passe
ui.admin.user.createpanel.header=Cr\u00e9er un nouvel utilisateur
ui.admin.user.delete.confirm=Etes vous s\u00fbr de vouloir supprimer cet utilisateur?
ui.admin.user.delete.failed.label=La suppression de l'utilisateur a \u00e9chou\u00e9
ui.admin.user.delete.label=Supprimer cet utilisateur
ui.admin.user.editlink=Modifier
ui.admin.user.groupmembership.header=Groupe(s) d'appartenance
ui.admin.user.password.header=Mise \u00e0 jour des informations de s\u00e9curit\u00e9
ui.admin.user.search.header=Rechercher
ui.admin.user.summarypanel.createUser=Cr\u00e9er un nouvel utilisateur
ui.admin.user.summarypanel.header=Table des mati\u00e8res
ui.admin.user.summarypanel.totalusers=Total des utilisateurs:
ui.admin.user.useredit.header=Modifier les informations de l'utilisateur
ui.admin.user.userinfo.header=Informations de l'utilisateur
ui.admin.user.userpasswordform.answer=R\u00e9ponse:<br>(<font size "-1">Ne pas remplir pour conserver la r\u00e9ponse actuelle</font>)
ui.admin.user.userpasswordform.confirmpasswordlabel=Confirmer le mot de passe:
ui.admin.user.userpasswordform.passwordlabel=Mot de passe:
ui.admin.user.userpasswordform.question=Question:
ui.admin.user.userpasswordform.submit=Changer
ui.admin.tab.applications=
ui.admin.applications.tree.heading=
ui.admin.applications.url.validation.not_blank=The URL of an application instance can is mandatory.
ui.admin.applications.url.valiation.minmaxlength=The length of an URL of an application instance must be between 1 and 100 characters.
ui.admin.applications.title.validation.not_blank=Title is mandatory for an application instance.
ui.admin.applications.title.valiation.minmaxlength=The minimum length of the title of an applicatio instance is one character, the maximum length are 200 characters
ui.admin.applications.desc.valiation.minmaxlength=The maximum length of a descrption of an application instance are 4000 characters.
ui.admin.applications.url.label=URL
ui.admin.applications.title.label=Title
ui.admin.applications.desc.label=Description
ui.admin.applications.url.validation.url_already_in_use=DescriptionThe provided URL is already in use
ui.admin.applications.url.validation.no_slash_allowed=The URL fragement may not contain slashes
ui.admin.applications.ApplicationInstancePane.title.label=Title of the instance
ui.admin.applications.ApplicationInstancePane.parent_app.label=Parent application
ui.admin.cancel=Cancel
ui.admin.cancel_msg=Submission cancelled
ui.admin.groups.ID_is_null=
ui.admin.user.userpasswordform.retrieving_user_failed=Impossible de retrouver l'utilisateur
ui.admin.groups.couldnt_find_specified_group=Impossible de trouver le groupe sp\u00e9cifi\u00e9