From beb702efd97bd9a56aedf33e2dd536bb1aca9cef Mon Sep 17 00:00:00 2001 From: jensp Date: Fri, 26 Feb 2016 17:36:33 +0000 Subject: [PATCH] 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 --- .../src/main/resources/log4j2.xml | 7 +- .../bebop/AbstractSingleSelectionModel.java | 74 ++++++----- .../main/java/com/arsdigita/bebop/Label.java | 2 +- .../bebop/ParameterSingleSelectionModel.java | 46 ++++--- .../arsdigita/bebop/SingleSelectionModel.java | 20 +-- .../main/java/com/arsdigita/bebop/Text.java | 115 +++++++++++++++++ .../bebop/event/EventListenerList.java | 59 +++++---- .../bebop/parameters/LongParameter.java | 2 + .../bebop/parameters/ParameterModel.java | 22 ++++ .../templating/SimpleURIResolver.java | 6 +- .../arsdigita/ui/CcmObjectSelectionModel.java | 117 ++++++++++++++++++ .../arsdigita/ui/admin/AdminConstants.java | 7 +- .../ui/admin/usersgroupsroles/UserAdmin.java | 53 +++++++- .../usersgroupsroles/UsersGroupsRolesTab.java | 10 -- .../ui/admin/usersgroupsroles/UsersTable.java | 66 +++++++++- .../core/AbstractEntityRepository.java | 70 ++++++++++- .../main/java/org/libreccm/security/User.java | 9 +- .../org/libreccm/security/UserRepository.java | 19 ++- .../ui/admin/AdminResources.properties | 4 + .../ui/admin/AdminResources_de.properties | 4 + .../ui/admin/AdminResources_en.properties | 4 + .../ui/admin/AdminResources_fr.properties | 4 + 22 files changed, 599 insertions(+), 121 deletions(-) create mode 100644 ccm-core/src/main/java/com/arsdigita/bebop/Text.java create mode 100644 ccm-core/src/main/java/com/arsdigita/ui/CcmObjectSelectionModel.java diff --git a/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml b/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml index 3ac04b858..c58bb8918 100644 --- a/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml +++ b/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml @@ -6,7 +6,7 @@ - + + level="info"> + + diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/AbstractSingleSelectionModel.java b/ccm-core/src/main/java/com/arsdigita/bebop/AbstractSingleSelectionModel.java index 16561cd39..dca4ae8e1 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/AbstractSingleSelectionModel.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/AbstractSingleSelectionModel.java @@ -26,20 +26,28 @@ import com.arsdigita.bebop.event.EventListenerList; import com.arsdigita.util.Assert; import com.arsdigita.util.Lockable; -/** - * A standard implementation of SingleSelectionModel and - * Lockable. 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 SingleSelectionModel and + * Lockable. 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 + * + * @author Unknown + * @author Jens Pelzetter (jensp) + */ +public abstract class AbstractSingleSelectionModel + implements SingleSelectionModel, 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 true if there is a selected element. * * @param state the state of the current request + * * @return true if there is a selected component; - * false otherwise. + * false 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 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; } + } diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/Label.java b/ccm-core/src/main/java/com/arsdigita/bebop/Label.java index 0137aba6d..1895d5e68 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/Label.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/Label.java @@ -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 diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/ParameterSingleSelectionModel.java b/ccm-core/src/main/java/com/arsdigita/bebop/ParameterSingleSelectionModel.java index b9a37bc7d..7128b63e6 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/ParameterSingleSelectionModel.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/ParameterSingleSelectionModel.java @@ -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. *

* * A typical use case for this class is as follows. - *

public TheConstructor() {
+ * 
+ *
+ * 
+ * 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);
- * }
+ * } + *
+ *
+ *
+ * + * jensp 2016-02-26: Added generics + * + * @param 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 + extends AbstractSingleSelectionModel { - - 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 PageState value + * * @return a String 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); } + } diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/SingleSelectionModel.java b/ccm-core/src/main/java/com/arsdigita/bebop/SingleSelectionModel.java index 02d1f3daf..da5ca9eb5 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/SingleSelectionModel.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/SingleSelectionModel.java @@ -38,10 +38,14 @@ import com.arsdigita.bebop.parameters.ParameterModel; * key's toString 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 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 { /** * Returns true if there is a selected element. @@ -58,7 +62,7 @@ public interface SingleSelectionModel { * @param state a PageState value * @return a String value. */ - Object getSelectedKey(PageState state); + T getSelectedKey(PageState state); /** * Sets the selected key. If key is not in the collection of @@ -70,7 +74,7 @@ public interface SingleSelectionModel { * @throws IllegalArgumentException if the supplied key 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 * stateChanged 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 diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/Text.java b/ccm-core/src/main/java/com/arsdigita/bebop/Text.java new file mode 100644 index 000000000..3261f8648 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/bebop/Text.java @@ -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 Jens Pelzetter + */ +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; + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/event/EventListenerList.java b/ccm-core/src/main/java/com/arsdigita/bebop/event/EventListenerList.java index 8125c5d2f..2a1e756f7 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/event/EventListenerList.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/event/EventListenerList.java @@ -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 l. * - * @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 * EventListenerList}. * - * @param t The class of the event listeners that should be returned + * @param + * @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 Iterator getListenerIterator(final Class type) { + return new EventListenerIterator<>(type); } - private class EventListenerIterator implements Iterator { + private class EventListenerIterator implements Iterator { /** * 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 type; - EventListenerIterator(Class t) { + EventListenerIterator(Class 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 + * + * @author Jens Pelzetter + + */ +public class CcmObjectSelectionModel + implements SingleSelectionModel{ + + private final Class clazz; + private final SingleSelectionModel model; + + public CcmObjectSelectionModel(final LongParameter parameter) { + this(null, parameter); + } + + public CcmObjectSelectionModel(final String parameterName) { + this(null, new LongParameter(parameterName)); + } + +// public CcmObjectSelectionModel(final SingleSelectionModel model ) { +// this(null, model); +// } +// + public CcmObjectSelectionModel(final Class clazz, + final String parameterName) { + this(clazz, new LongParameter(parameterName)); + } + + public CcmObjectSelectionModel(final Class clazz, + final LongParameter parameter) { + this(clazz, new ParameterSingleSelectionModel<>(parameter)); + } + + public CcmObjectSelectionModel(final Class clazz, + final SingleSelectionModel 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. + } + + + +} diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminConstants.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminConstants.java index c16d2ed95..357f76708 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminConstants.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminConstants.java @@ -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. diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UserAdmin.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UserAdmin.java index 39545e3bb..55e7de3b0 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UserAdmin.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UserAdmin.java @@ -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 Jens Pelzetter */ public class UserAdmin extends BoxPanel { - + + private final ParameterSingleSelectionModel 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); } - + } diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersGroupsRolesTab.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersGroupsRolesTab.java index 3f0e93e66..9fbba3493 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersGroupsRolesTab.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersGroupsRolesTab.java @@ -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) { diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersTable.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersTable.java index a52ff8fc0..2f4e79a66 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersTable.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/usersgroupsroles/UsersTable.java @@ -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 selectedUserId; + + public UsersTable(final TextField usersTableFilter, + final ParameterSingleSelectionModel 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 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 diff --git a/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java b/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java index 03b98980d..60f43467b 100644 --- a/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java +++ b/ccm-core/src/main/java/org/libreccm/core/AbstractEntityRepository.java @@ -82,19 +82,19 @@ public abstract class AbstractEntityRepository { protected void applyDefaultEntityGraph(final TypedQuery 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 { public List 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 findAll(final String entityGraphName) { + @SuppressWarnings("unchecked") + final EntityGraph entityGraph = (EntityGraph) entityManager + .getEntityGraph( + entityGraphName); + + return findAll(entityGraph); + } + + public List findAll(final EntityGraph 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 createCriteriaQuery() { final CriteriaBuilder criteriaBuilder = entityManager .getCriteriaBuilder(); final CriteriaQuery criteriaQuery = criteriaBuilder.createQuery( getEntityClass()); final Root root = criteriaQuery.from(getEntityClass()); - criteriaQuery.select(root); + return criteriaQuery.select(root); + } + public CriteriaBuilder getCriteriaBuilder() { + return entityManager.getCriteriaBuilder(); + } + + public List executeCriteriaQuery(final CriteriaQuery criteriaQuery) { + if (hasDefaultEntityGraph()) { + return executeCriteriaQuery(criteriaQuery, getDefaultEntityGraph()); + } else { + final TypedQuery query = entityManager.createQuery(criteriaQuery); + return query.getResultList(); + } + } + + public List executeCriteriaQuery(final CriteriaQuery criteriaQuery, + final String graphName) { + @SuppressWarnings("unchecked") + final EntityGraph entityGraph = (EntityGraph< E>) entityManager + .getEntityGraph( + graphName); + return executeCriteriaQuery(criteriaQuery, entityGraph); + } + + public List executeCriteriaQuery(final CriteriaQuery criteriaQuery, + final EntityGraph entityGraph) { final TypedQuery query = entityManager.createQuery(criteriaQuery); + query.setHint(FETCH_GRAPH_HINT_KEY, entityGraph); return query.getResultList(); } @@ -251,4 +296,19 @@ public abstract class AbstractEntityRepository { 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())); + } + } + } diff --git a/ccm-core/src/main/java/org/libreccm/security/User.java b/ccm-core/src/main/java/org/libreccm/security/User.java index 7c9235325..0e875d15f 100644 --- a/ccm-core/src/main/java/org/libreccm/security/User.java +++ b/ccm-core/src/main/java/org/libreccm/security/User.java @@ -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( diff --git a/ccm-core/src/main/java/org/libreccm/security/UserRepository.java b/ccm-core/src/main/java/org/libreccm/security/UserRepository.java index dff354952..bd0385597 100644 --- a/ccm-core/src/main/java/org/libreccm/security/UserRepository.java +++ b/ccm-core/src/main/java/org/libreccm/security/UserRepository.java @@ -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 { applyDefaultEntityGraph(query); return getSingleResultOrNull(query); - -// final List 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 { "User.findByEmailAddress", User.class); query.setParameter("emailAddress", emailAddress); query.setHint(FETCH_GRAPH_HINT_KEY, entityGraph); - + return getSingleResultOrNull(query); } + public List filtered(final String term) { + final TypedQuery query = getEntityManager().createNamedQuery( + "User.filterByNameAndEmail", User.class); + query.setParameter("term", term); + + return query.getResultList(); + } + } diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties index 1e2524411..17d321d1f 100644 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources.properties @@ -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 diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties index fbfb4aae6..0f803f74b 100644 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_de.properties @@ -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 diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties index f87d5fb69..9554553b6 100755 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_en.properties @@ -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 diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties index 61a59cb7d..bba06e6db 100755 --- a/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties +++ b/ccm-core/src/main/resources/com/arsdigita/ui/admin/AdminResources_fr.properties @@ -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