CCM NG: Primarly bug fixing for the selection models

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3897 8810af33-2d31-482b-a856-94f89814c4df
pull/2/head
jensp 2016-02-26 17:36:33 +00:00
parent 2d30f3d69f
commit beb702efd9
22 changed files with 599 additions and 121 deletions

View File

@ -6,7 +6,7 @@
</Console>
</Appenders>
<Loggers>
<Root level="info">
<Root level="warn">
<AppenderRef ref="Console"/>
</Root>
<Logger name="com.arsdigita.ui.admin.AdminServlet"
@ -22,7 +22,10 @@
level="debug">
</Logger>
<Logger name="com.arsdigita.templating.PatternStylesheetResolver"
level="debug">
level="info">
</Logger>
<Logger name="com.arsdigita.templating.SimpleURIResolver"
level="warn">
</Logger>
<Logger name="com.arsdigita.web.CCMDispatcherServlet"
level="debug">

View File

@ -26,20 +26,28 @@ import com.arsdigita.bebop.event.EventListenerList;
import com.arsdigita.util.Assert;
import com.arsdigita.util.Lockable;
/**
* A standard implementation of <code>SingleSelectionModel</code> and
* <code>Lockable</code>. Those wishing to define a SingleSelectionModel
* will ordinarily want to extend this class.
*
* @version $Id: AbstractSingleSelectionModel.java 287 2005-02-22 00:29:02Z sskracic $
*/
public abstract class AbstractSingleSelectionModel
implements SingleSelectionModel, Lockable {
import java.util.stream.Stream;
private EventListenerList m_listeners;
/**
* A standard implementation of <code>SingleSelectionModel</code> and
* <code>Lockable</code>. Those wishing to define a SingleSelectionModel will
* ordinarily want to extend this class.
*
* jensp: Added generics and Java 8 streams instead of using an iterator.
*
* @param <T>
*
* @author Unknown
* @author Jens Pelzetter (jensp)
*/
public abstract class AbstractSingleSelectionModel<T>
implements SingleSelectionModel<T>, Lockable {
private final EventListenerList m_listeners;
private boolean m_locked;
/** Creates a new AbstractSingleSelectionModel.
/**
* Creates a new AbstractSingleSelectionModel.
*/
public AbstractSingleSelectionModel() {
m_listeners = new EventListenerList();
@ -49,51 +57,57 @@ public abstract class AbstractSingleSelectionModel
* Returns <code>true</code> if there is a selected element.
*
* @param state the state of the current request
*
* @return <code>true</code> if there is a selected component;
* <code>false</code> otherwise.
* <code>false</code> otherwise.
*/
public boolean isSelected(PageState state) {
@Override
public boolean isSelected(final PageState state) {
return getSelectedKey(state) != null;
}
public abstract Object getSelectedKey(PageState state);
@Override
public abstract T getSelectedKey(final PageState state);
public abstract void setSelectedKey(PageState state, Object key);
@Override
public abstract void setSelectedKey(final PageState state, final T key);
public void clearSelection(PageState state) {
@Override
public void clearSelection(final PageState state) {
setSelectedKey(state, null);
}
// Selection change events
public void addChangeListener(ChangeListener l) {
@Override
public void addChangeListener(final ChangeListener changeListener) {
Assert.isUnlocked(this);
m_listeners.add(ChangeListener.class, l);
m_listeners.add(ChangeListener.class, changeListener);
}
public void removeChangeListener(ChangeListener l) {
@Override
public void removeChangeListener(final ChangeListener changeListener) {
Assert.isUnlocked(this);
m_listeners.remove(ChangeListener.class, l);
m_listeners.remove(ChangeListener.class, changeListener);
}
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);
protected void fireStateChanged(final PageState state) {
final ChangeEvent event = new ChangeEvent(this, state);
final Iterator<ChangeListener> iterator = m_listeners
.getListenerIterator(ChangeListener.class);
while(iterator.hasNext()) {
iterator.next().stateChanged(event);
}
}
// implement Lockable
@Override
public void lock() {
m_locked = true;
}
@Override
public final boolean isLocked() {
return m_locked;
}
}

View File

@ -29,7 +29,7 @@ import com.arsdigita.xml.Element;
* of certain parts of the screen. Therefore the label has to use a
* GlobalizedMessage for the information presented.
*
* A Label is meant to provide semantically relevant informatin and may not be
* A Label is meant to provide semantically relevant information and may not be
* used for fixed arbitrary Text. Use Embedded instead.
*
* (Previous usage: can be used to generate either some static, fixed

View File

@ -22,12 +22,15 @@ import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.util.Assert;
/**
* An implementation of {@link SingleSelectionModel} that uses
* a state parameter for managing the currently selected key.
* An implementation of {@link SingleSelectionModel} that uses a state parameter
* for managing the currently selected key.
* <p>
*
* A typical use case for this class is as follows.
* <blockquote><pre><code>public TheConstructor() {
* <blockquote>
* <pre>
* <code>
* public TheConstructor() {
* m_parameter = new StringParameter("my_key");
* m_sel = new ParameterSingleSelectionModel(m_parameter);
* }
@ -35,22 +38,29 @@ import com.arsdigita.util.Assert;
* public void register(Page p) {
* p.addComponent(this);
* p.addComponentStateParam(this, m_param);
* }</code></pre></blockquote>
* }
* </code>
* </pre>
* </blockquote>
*
* jensp 2016-02-26: Added generics
*
* @param <T> Generics parameter
*
* @author Stanislav Freidin
* @version $Id: ParameterSingleSelectionModel.java 287 2005-02-22 00:29:02Z sskracic $
* @author Jens Pelzetter
*
*/
public class ParameterSingleSelectionModel
extends AbstractSingleSelectionModel {
public class ParameterSingleSelectionModel<T>
extends AbstractSingleSelectionModel<T> {
private ParameterModel m_parameter;
private final ParameterModel m_parameter;
/**
* Constructs a new ParameterSingleSelectionModel.
*
* @param m the parameter model that will be used to
* keep track of the currently selected key
* @param m the parameter model that will be used to keep track of the
* currently selected key
*/
public ParameterSingleSelectionModel(ParameterModel m) {
super();
@ -62,17 +72,21 @@ public class ParameterSingleSelectionModel
* Returns the key that identifies the selected element.
*
* @param state a <code>PageState</code> value
*
* @return a <code>String</code> value.
*/
public Object getSelectedKey(PageState state) {
@Override
@SuppressWarnings("unchecked")
public T getSelectedKey(final PageState state) {
final FormModel model = state.getPage().getStateModel();
if (model.containsFormParam(m_parameter)) {
return state.getValue(m_parameter);
return (T) state.getValue(m_parameter);
} else {
return null;
}
}
@Override
public final ParameterModel getStateParameter() {
return m_parameter;
}
@ -80,10 +94,11 @@ public class ParameterSingleSelectionModel
/**
* Set the selected key.
*
* @param state represents the state of the current request
* @param state represents the state of the current request
* @param newKey the new selected key
*/
public void setSelectedKey(PageState state, Object newKey) {
@Override
public void setSelectedKey(final PageState state, final Object newKey) {
final Object oldKey = getSelectedKey(state);
if (Assert.isEnabled()) {
@ -103,4 +118,5 @@ public class ParameterSingleSelectionModel
fireStateChanged(state);
}
}

View File

@ -38,10 +38,14 @@ import com.arsdigita.bebop.parameters.ParameterModel;
* key's <code>toString</code> method produces a representation of the key
* that can be used in URL strings and hidden form controls.
*
* Edit for CCM NG: Added generics.
*
* @param <T> Type for the key
*
* @author David Lutterkort
* @version $Id: SingleSelectionModel.java 287 2005-02-22 00:29:02Z sskracic $
* @author Jens Pelzetter
*/
public interface SingleSelectionModel {
public interface SingleSelectionModel<T> {
/**
* Returns <code>true</code> if there is a selected element.
@ -58,7 +62,7 @@ public interface SingleSelectionModel {
* @param state a <code>PageState</code> value
* @return a <code>String</code> value.
*/
Object getSelectedKey(PageState state);
T getSelectedKey(PageState state);
/**
* Sets the selected key. If <code>key</code> is not in the collection of
@ -70,7 +74,7 @@ public interface SingleSelectionModel {
* @throws IllegalArgumentException if the supplied <code>key</code> can not
* be selected in the context of the current request.
*/
void setSelectedKey(PageState state, Object key);
void setSelectedKey(PageState state, T key);
/**
* Clears the selection.
@ -84,16 +88,16 @@ public interface SingleSelectionModel {
* Adds a change listener to the model. The listener's
* <code>stateChanged</code> method is called whenever the selected key changes.
*
* @param l a listener to notify when the selected key changes
* @param changeListener a listener to notify when the selected key changes
*/
void addChangeListener(ChangeListener l);
void addChangeListener(ChangeListener changeListener);
/**
* Removes a change listener from the model.
*
* @param l the listener to remove
* @param changeListener the listener to remove
*/
void removeChangeListener(ChangeListener l);
void removeChangeListener(ChangeListener changeListener);
/**
* Returns the state parameter that will be used to keep track

View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2016 LibreCCM Foundation.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package com.arsdigita.bebop;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.xml.Element;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class Text extends SimpleComponent {
private String text;
private boolean outputEscaped;
private PrintListener printListener;
public Text() {
this("");
}
public Text(final String text) {
this.text = text;
outputEscaped = true;
}
public Text(final PrintListener printListener) {
this();
this.printListener = printListener;
}
public String getText() {
return text;
}
public void setText(final String text) {
this.text = text;
}
public boolean isOutputEscaped() {
return outputEscaped;
}
public void setOutputEscaped(final boolean outputEscaped) {
this.outputEscaped = outputEscaped;
}
public void setPrintListener(final PrintListener printListener) {
if (printListener == null) {
throw new IllegalArgumentException("PrintListener can't be null");
}
this.printListener = printListener;
}
@Override
public void generateXML(final PageState state, final Element parent) {
if (!isVisible(state)) {
return;
}
final Text target = firePrintEvent(state);
final Element textElem = parent.newChildElement("bebop:text",
BEBOP_XML_NS);
target.exportAttributes(textElem);
if (outputEscaped) {
textElem.addAttribute("escape", "no");
} else {
textElem.addAttribute("escape", "yes");
}
textElem.setText(target.getText());
}
protected Text firePrintEvent(final PageState state) {
final Text component;
if (printListener == null) {
component = this;
} else {
try {
component = (Text) this.clone();
printListener.prepare(new PrintEvent(this, state, component));
} catch (CloneNotSupportedException ex) {
throw new UncheckedWrapperException(
"Could not clone Text component for PrintListener. "
+ "This propaby indicates a serious programming error.");
}
}
return component;
}
}

View File

@ -28,22 +28,25 @@ import java.util.NoSuchElementException;
*/
public class EventListenerList extends javax.swing.event.EventListenerList {
private static final long serialVersionUID = -1930203818146602205L;
/**
* Append all the event listeners from <code>l</code>.
*
* @param l The list of listeners to copy from
* @param list The list of listeners to copy from
*
* @pre l != null
*/
public void addAll(EventListenerList l) {
public void addAll(final EventListenerList list) {
if ( l.listenerList.length == 0 )
if ( list.listenerList.length == 0 ) {
return;
}
Object[] tmp = new Object[listenerList.length + l.listenerList.length];
Object[] tmp = new Object[listenerList.length + list.listenerList.length];
System.arraycopy(listenerList, 0, tmp, 0, listenerList.length);
System.arraycopy(l.listenerList, 0,
tmp, listenerList.length, l.listenerList.length);
System.arraycopy(list.listenerList, 0,
tmp, listenerList.length, list.listenerList.length);
listenerList = tmp;
}
@ -53,15 +56,17 @@ public class EventListenerList extends javax.swing.event.EventListenerList {
* {@link javax.swing.event.EventListenerList Swing's
* <code>EventListenerList</code>}.
*
* @param t The class of the event listeners that should be returned
* @param <T>
* @param type The class of the event listeners that should be returned
* @return
*
* @pre t != null
* */
public Iterator getListenerIterator(final Class t) {
return new EventListenerIterator(t);
public <T> Iterator<T> getListenerIterator(final Class<T> type) {
return new EventListenerIterator<>(type);
}
private class EventListenerIterator implements Iterator {
private class EventListenerIterator<T> implements Iterator<T> {
/**
* The listener we will return with the next call to next().
@ -69,31 +74,35 @@ public class EventListenerList extends javax.swing.event.EventListenerList {
* matching listeners have been returned, in which case _next
* is -1
* */
private int _count;
private int _next;
private Class _t;
private final int count;
private int next;
private final Class<T> type;
EventListenerIterator(Class t) {
EventListenerIterator(Class<T> type) {
_count = getListenerList().length;
_next = -2;
_t = t;
count = getListenerList().length;
next = -2;
this.type = type;
findNext();
}
@Override
public boolean hasNext() {
return (_next < _count);
return (next < count);
}
public Object next() throws NoSuchElementException {
@Override
@SuppressWarnings("unchecked")
public T next() throws NoSuchElementException {
if ( ! hasNext() ) {
throw new NoSuchElementException("Iterator exhausted");
}
int result = _next;
int result = next;
findNext();
return getListenerList()[result+1];
return (T) getListenerList()[result+1];
}
@Override
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException("Removal not supported");
}
@ -108,14 +117,14 @@ public class EventListenerList extends javax.swing.event.EventListenerList {
* */
private void findNext() {
for (int i = _next+2; i<_count; i+=2) {
for (int i = next+2; i<count; i+=2) {
if (getListenerList()[i] == _t) {
_next = i;
if (getListenerList()[i] == type) {
next = i;
return;
}
}
_next = _count;
next = count;
}
}
}

View File

@ -32,6 +32,7 @@ public class LongParameter extends NumberParameter {
super(name);
}
@Override
public Object unmarshal(String encoded) {
try {
return new Long(encoded);
@ -43,6 +44,7 @@ public class LongParameter extends NumberParameter {
}
}
@Override
public Class getValueClass() {
return Long.class;
}

View File

@ -33,6 +33,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import java.util.Objects;
/**
* Represents the abstract model for a form parameter object. This
* class must be subclassed for each specific data type.
@ -495,4 +497,24 @@ public abstract class ParameterModel implements Lockable {
}
return false;
}
@Override
public boolean equals(final Object other) {
if (other == null) {
return false;
}
if (!(other instanceof ParameterModel)) {
return false;
}
return m_name.equals(((ParameterModel) other).getName());
}
@Override
public int hashCode() {
int hash = 7;
hash = 73 * hash + Objects.hashCode(this.m_name);
return hash;
}
}

View File

@ -18,6 +18,8 @@
*/
package com.arsdigita.templating;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@ -32,7 +34,7 @@ import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;
import org.apache.log4j.Logger;
import org.apache.logging.log4j.Logger;
/**
* An implementation of the URIResolver interface that keeps track of all the
@ -44,7 +46,7 @@ import org.apache.log4j.Logger;
*/
final class SimpleURIResolver implements URIResolver {
private static final Logger s_log = Logger.getLogger
private static final Logger s_log = LogManager.getLogger
(SimpleURIResolver.class);
private final Set m_uniqueStylesheetURIs;

View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 2016 LibreCCM Foundation.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package com.arsdigita.ui;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.SingleSelectionModel;
import com.arsdigita.bebop.event.ChangeListener;
import com.arsdigita.bebop.parameters.LongParameter;
import com.arsdigita.bebop.parameters.ParameterModel;
import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.CcmObject;
import org.libreccm.core.CcmObjectRepository;
/**
* @param <T>
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class CcmObjectSelectionModel<T extends CcmObject>
implements SingleSelectionModel<Long>{
private final Class<T> clazz;
private final SingleSelectionModel<Long> model;
public CcmObjectSelectionModel(final LongParameter parameter) {
this(null, parameter);
}
public CcmObjectSelectionModel(final String parameterName) {
this(null, new LongParameter(parameterName));
}
// public CcmObjectSelectionModel(final SingleSelectionModel<T> model ) {
// this(null, model);
// }
//
public CcmObjectSelectionModel(final Class<T> clazz,
final String parameterName) {
this(clazz, new LongParameter(parameterName));
}
public CcmObjectSelectionModel(final Class<T> clazz,
final LongParameter parameter) {
this(clazz, new ParameterSingleSelectionModel<>(parameter));
}
public CcmObjectSelectionModel(final Class<T> clazz,
final SingleSelectionModel<Long> model) {
this.clazz = clazz;
this.model = model;
}
@Override
public boolean isSelected(final PageState state) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public Long getSelectedKey(final PageState state) {
return model.getSelectedKey(state);
}
@Override
public void setSelectedKey(final PageState state, final Long key) {
model.setSelectedKey(state, key);
}
public T getSelectedObject(final PageState state) {
final Long key = getSelectedKey(state);
final CcmObjectRepository repository = CdiUtil.createCdiUtil().findBean(
CcmObjectRepository.class);
//final T object = repository.findById(key);
throw new UnsupportedOperationException();
}
@Override
public void clearSelection(PageState state) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void addChangeListener(ChangeListener changeListener) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void removeChangeListener(ChangeListener changeListener) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public ParameterModel getStateParameter() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}

View File

@ -20,6 +20,7 @@ package com.arsdigita.ui.admin;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.parameters.BigDecimalParameter;
import com.arsdigita.bebop.parameters.LongParameter;
import com.arsdigita.globalization.GlobalizedMessage;
/**
@ -100,11 +101,11 @@ interface AdminConstants {
/**
* Global state parameters.
*/
BigDecimalParameter GROUP_ID_PARAM = new BigDecimalParameter("group_id");
LongParameter GROUP_ID_PARAM = new LongParameter("group_id");
BigDecimalParameter APPLICATIONS_ID_PARAM = new BigDecimalParameter("application_id");
LongParameter APPLICATIONS_ID_PARAM = new LongParameter("application_id");
BigDecimalParameter USER_ID_PARAM = new BigDecimalParameter("user_id");
LongParameter USER_ID_PARAM = new LongParameter("user_id");
/**
* User summary panel.

View File

@ -18,19 +18,64 @@
*/
package com.arsdigita.ui.admin.usersgroupsroles;
import com.arsdigita.bebop.ActionLink;
import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.Text;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.LongParameter;
import com.arsdigita.globalization.GlobalizedMessage;
import static com.arsdigita.ui.admin.AdminUiConstants.*;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public class UserAdmin extends BoxPanel {
private final ParameterSingleSelectionModel<String> selectedUserId;
private final TextField usersTableFilter;
public UserAdmin() {
super();
//add(new Label("User Admin class"));
add(new UsersTable());
final Form filterForm = new Form("usersTableFilterForm");
usersTableFilter = new TextField("usersTableFilter");
usersTableFilter.setLabel(new GlobalizedMessage(
"ui.admin.users.table.filter.term", ADMIN_BUNDLE));
filterForm.add(usersTableFilter);
filterForm.add(new Submit(new GlobalizedMessage(
"ui.admin.users.table.filter.submit", ADMIN_BUNDLE)));
final ActionLink clearLink = new ActionLink(new GlobalizedMessage(
"ui.admin.users.table.filter.clear", ADMIN_BUNDLE));
clearLink.addActionListener((e) -> {
final PageState state = e.getPageState();
usersTableFilter.setValue(state, null);
});
filterForm.add(clearLink);
add(filterForm);
selectedUserId = new ParameterSingleSelectionModel<>(USER_ID_PARAM);
final UsersTable usersTable = new UsersTable(usersTableFilter,
selectedUserId);
add(usersTable);
final Text text = new Text();
text.setPrintListener((final PrintEvent e) -> {
final Text target = (Text) e.getTarget();
final PageState state = e.getPageState();
if (selectedUserId.isSelected(state)) {
target.setText(selectedUserId.getSelectedKey(state));
}
});
add(text);
}
}

View File

@ -94,16 +94,6 @@ public class UsersGroupsRolesTab extends LayoutPanel {
setBody(body);
}
private SimpleContainer buildUserAdmin() {
final BoxPanel panel = new BoxPanel();
panel.add(new Label("User Admin"));
return panel;
}
private void addSection(final Label label,
final Component component,
final BoxPanel panel) {

View File

@ -18,9 +18,17 @@
*/
package com.arsdigita.ui.admin.usersgroupsroles;
import com.arsdigita.bebop.Component;
import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.ParameterSingleSelectionModel;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.event.TableActionAdapter;
import com.arsdigita.bebop.event.TableActionEvent;
import com.arsdigita.bebop.event.TableActionListener;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableColumn;
import com.arsdigita.bebop.table.TableColumnModel;
import com.arsdigita.bebop.table.TableModel;
@ -52,9 +60,16 @@ public class UsersTable extends Table {
private static final int COL_PRIMARY_EMAIL = 3;
private static final int COL_BANNED = 4;
public UsersTable() {
private final TextField usersTableFilter;
private final ParameterSingleSelectionModel<String> selectedUserId;
public UsersTable(final TextField usersTableFilter,
final ParameterSingleSelectionModel<String> selectedUserId) {
super();
this.usersTableFilter = usersTableFilter;
this.selectedUserId = selectedUserId;
setEmptyView(new Label(new GlobalizedMessage(
"ui.admin.users.table.no_users", ADMIN_BUNDLE)));
@ -79,7 +94,36 @@ public class UsersTable extends Table {
COL_BANNED,
new Label(new GlobalizedMessage(
"ui.admin.users.table.banned", ADMIN_BUNDLE))));
columnModel.get(COL_SCREEN_NAME).setCellRenderer(new TableCellRenderer() {
@Override
public Component getComponent(final Table table,
final PageState state,
final Object value,
final boolean isSelected,
final Object key,
final int row,
final int column) {
return new ControlLink((String) value);
}
});
addTableActionListener(new TableActionListener() {
@Override
public void cellSelected(final TableActionEvent event) {
final String key = (String) event.getRowKey();
selectedUserId.setSelectedKey(event.getPageState(), key);
}
@Override
public void headSelected(final TableActionEvent event) {
//Nothing
}
});
setModelBuilder(new UsersTableModelBuilder());
}
@ -90,7 +134,7 @@ public class UsersTable extends Table {
public TableModel makeModel(final Table table, final PageState state) {
table.getRowSelectionModel().clearSelection(state);
return new UsersTableModel();
return new UsersTableModel(state);
}
}
@ -100,12 +144,24 @@ public class UsersTable extends Table {
private final List<User> users;
private int index = -1;
public UsersTableModel() {
public UsersTableModel(final PageState state) {
LOGGER.debug("Creating UsersTableModel...");
final String filterTerm = (String) usersTableFilter
.getValue(state);
LOGGER.debug("Value of filter is: \"{}\"", filterTerm);
final UserRepository userRepository = CdiUtil.createCdiUtil()
.findBean(UserRepository.class);
users = userRepository.findAll();
LOGGER.debug("Found {} users in database.", users.size());
if (filterTerm == null || filterTerm.isEmpty()) {
users = userRepository.findAll();
LOGGER.debug("Found {} users in database.", users.size());
} else {
users = userRepository.filtered(filterTerm);
LOGGER.debug("Found {} users in database which match the "
+ "filter \"{}\".",
users.size(),
filterTerm);
}
}
@Override

View File

@ -82,19 +82,19 @@ public abstract class AbstractEntityRepository<K, E> {
protected void applyDefaultEntityGraph(final TypedQuery<E> query) {
if (getEntityClass().isAnnotationPresent(DefaultEntityGraph.class)) {
LOGGER.debug("The following EntityGraphs are available for the "
+ "entity class {}:",
+ "entity class {}:",
getEntityClass().getName());
getEntityManager().getEntityGraphs(getEntityClass()).stream()
.forEach(g -> LOGGER.debug("\t{}", g.getName()));
LOGGER.debug("Entity class {} has default entity graphs:",
LOGGER.debug("Entity class {} has default entity graphs:",
getEntityClass().getName());
LOGGER.debug("Applying entity graph {}",
getEntityClass().getAnnotation(
DefaultEntityGraph.class).value());
query.setHint(FETCH_GRAPH_HINT_KEY,
entityManager.getEntityGraph(
getEntityClass().getAnnotation(
DefaultEntityGraph.class).value()));
getEntityClass().getAnnotation(
DefaultEntityGraph.class).value()));
}
}
@ -202,14 +202,59 @@ public abstract class AbstractEntityRepository<K, E> {
public List<E> findAll() {
// We are using the Critiera API here because otherwise we can't
// pass the type of the entity dynmacially.
return executeCriteriaQuery(createCriteriaQuery());
}
public List<E> findAll(final String entityGraphName) {
@SuppressWarnings("unchecked")
final EntityGraph<E> entityGraph = (EntityGraph<E>) entityManager
.getEntityGraph(
entityGraphName);
return findAll(entityGraph);
}
public List<E> findAll(final EntityGraph<E> entityGraph) {
// We are using the Critiera API here because otherwise we can't
// pass the type of the entity dynmacially.
return executeCriteriaQuery(createCriteriaQuery(), entityGraph);
}
public CriteriaQuery<E> createCriteriaQuery() {
final CriteriaBuilder criteriaBuilder = entityManager
.getCriteriaBuilder();
final CriteriaQuery<E> criteriaQuery = criteriaBuilder.createQuery(
getEntityClass());
final Root<E> root = criteriaQuery.from(getEntityClass());
criteriaQuery.select(root);
return criteriaQuery.select(root);
}
public CriteriaBuilder getCriteriaBuilder() {
return entityManager.getCriteriaBuilder();
}
public List<E> executeCriteriaQuery(final CriteriaQuery<E> criteriaQuery) {
if (hasDefaultEntityGraph()) {
return executeCriteriaQuery(criteriaQuery, getDefaultEntityGraph());
} else {
final TypedQuery<E> query = entityManager.createQuery(criteriaQuery);
return query.getResultList();
}
}
public List<E> executeCriteriaQuery(final CriteriaQuery<E> criteriaQuery,
final String graphName) {
@SuppressWarnings("unchecked")
final EntityGraph<E> entityGraph = (EntityGraph< E>) entityManager
.getEntityGraph(
graphName);
return executeCriteriaQuery(criteriaQuery, entityGraph);
}
public List<E> executeCriteriaQuery(final CriteriaQuery<E> criteriaQuery,
final EntityGraph<E> entityGraph) {
final TypedQuery<E> query = entityManager.createQuery(criteriaQuery);
query.setHint(FETCH_GRAPH_HINT_KEY, entityGraph);
return query.getResultList();
}
@ -251,4 +296,19 @@ public abstract class AbstractEntityRepository<K, E> {
entityManager.remove(entity);
}
protected boolean hasDefaultEntityGraph() {
return getEntityClass().isAnnotationPresent(DefaultEntityGraph.class);
}
protected String getDefaultEntityGraph() {
if (hasDefaultEntityGraph()) {
return getEntityClass().getAnnotation(DefaultEntityGraph.class)
.value();
} else {
throw new IllegalArgumentException(String.format(
"Entity class \"%s\" has no DefaultEntityGraph!",
getEntityClass().getName()));
}
}
}

View File

@ -67,7 +67,14 @@ import javax.xml.bind.annotation.XmlTransient;
query = "SELECT u FROM User u WHERE u.name = :name"),
@NamedQuery(name = "User.findByEmailAddress",
query = "SELECT u FROM User u WHERE "
+ "u.primaryEmailAddress.address = :emailAddress")
+ "u.primaryEmailAddress.address = :emailAddress"),
@NamedQuery(
name = "User.filterByNameAndEmail",
query = "SELECT u FROM User u WHERE "
+ "LOWER(u.name) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.givenName) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.familyName) LIKE CONCAT(LOWER(:term), '%') "
+ "OR LOWER(u.primaryEmailAddress.address) LIKE CONCAT('%', LOWER(:term), '%')")
})
@NamedEntityGraphs({
@NamedEntityGraph(

View File

@ -21,9 +21,7 @@ package org.libreccm.security;
import org.libreccm.core.AbstractEntityRepository;
import java.util.List;
import java.util.Optional;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityGraph;
import javax.persistence.TypedQuery;
@ -126,13 +124,6 @@ public class UserRepository extends AbstractEntityRepository<Long, User> {
applyDefaultEntityGraph(query);
return getSingleResultOrNull(query);
// final List<User> result = query.getResultList();
// if (result.isEmpty()) {
// return null;
// } else {
// return result.get(0);
// }
}
public User findByEmailAddress(final String emailAddress,
@ -150,8 +141,16 @@ public class UserRepository extends AbstractEntityRepository<Long, User> {
"User.findByEmailAddress", User.class);
query.setParameter("emailAddress", emailAddress);
query.setHint(FETCH_GRAPH_HINT_KEY, entityGraph);
return getSingleResultOrNull(query);
}
public List<User> filtered(final String term) {
final TypedQuery<User> query = getEntityManager().createNamedQuery(
"User.filterByNameAndEmail", User.class);
query.setParameter("term", term);
return query.getResultList();
}
}

View File

@ -165,3 +165,7 @@ ui.admin.users.table.givenname=Given name
ui.admin.users.table.familyname=Family name
ui.admin.users.table.primary_email=E-Mail
ui.admin.users.table.banned=Banned
ui.admin.users.table.filter.term=Filter users
ui.admin.users.table.filter.submit=Apply
ui.admin.users.table.no_users=No users matching users found.
ui.admin.users.table.filter.clear=Clear filter

View File

@ -165,3 +165,7 @@ ui.admin.users.table.givenname=Vorname
ui.admin.users.table.familyname=Familienname
ui.admin.users.table.primary_email=E-Mail
ui.admin.users.table.banned=Gesperrt
ui.admin.users.table.filter.term=Benutzer filtern
ui.admin.users.table.filter.submit=Anwenden
ui.admin.users.table.no_users=Keine auf den aktuellen Filter passenden Benutzer gefunden.
ui.admin.users.table.filter.clear=Filter zur\u00fccksetzen

View File

@ -138,3 +138,7 @@ ui.admin.users.table.givenname=Given name
ui.admin.users.table.familyname=Family name
ui.admin.users.table.primary_email=E-Mail
ui.admin.users.table.banned=Banned
ui.admin.users.table.filter.term=Filter users
ui.admin.users.table.filter.submit=Apply
ui.admin.users.table.no_users=No users matching users found.
ui.admin.users.table.filter.clear=Clear filter

View File

@ -129,3 +129,7 @@ ui.admin.users.table.givenname=Given name
ui.admin.users.table.familyname=Family name
ui.admin.users.table.primary_email=E-Mail
ui.admin.users.table.banned=Banned
ui.admin.users.table.filter.term=Filter users
ui.admin.users.table.filter.submit=Apply
ui.admin.users.table.no_users=No users matching users found.
ui.admin.users.table.filter.clear=Clear filter