diff --git a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ShortcutsAppManager.java b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ShortcutsAppManager.java new file mode 100644 index 000000000..85db5a545 --- /dev/null +++ b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ShortcutsAppManager.java @@ -0,0 +1,45 @@ +/* + * 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 org.libreccm.shortcuts; + +import org.libreccm.shortcuts.ui.AdminPanel; +import com.arsdigita.ui.admin.applications.AbstractSingletonApplicationManager; +import com.arsdigita.ui.admin.applications.ApplicationInstanceAwareContainer; + +/** + * + * @author Jens Pelzetter + * @version $Id$ + */ +public class ShortcutsAppManager extends AbstractSingletonApplicationManager{ + + public Class getApplication() { + return Shortcuts.class; + } + + public ApplicationInstanceAwareContainer getApplicationAdminForm() { + final ApplicationInstanceAwareContainer container = new ApplicationInstanceAwareContainer(); + container.add(new AdminPanel()); + + return container; + } + + + +} diff --git a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/AdminPanel.java b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/AdminPanel.java new file mode 100644 index 000000000..3d3a97a5e --- /dev/null +++ b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/AdminPanel.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 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 org.libreccm.shortcuts.ui; + + +import org.apache.log4j.Category; + +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.ParameterSingleSelectionModel; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.parameters.LongParameter; + +public class AdminPanel extends SimpleContainer { + + final private ParameterSingleSelectionModel m_shortcut = new ParameterSingleSelectionModel(new LongParameter("ShortcutID")); + + private static final Category log = Category.getInstance(AdminPanel.class + .getName()); + + public AdminPanel() { + add(new ShortcutForm(m_shortcut)); + add(new ShortcutsTable(m_shortcut)); + } + + @Override + public void register(Page p) { + super.register(p); + + p.addGlobalStateParam(m_shortcut.getStateParameter()); + } + +} + diff --git a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutForm.java b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutForm.java new file mode 100644 index 000000000..3c3478a3e --- /dev/null +++ b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutForm.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2015 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 org.libreccm.shortcuts.ui; + +import java.math.BigDecimal; + +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.FormValidationListener; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.ParameterListener; +import com.arsdigita.bebop.event.ParameterEvent; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.form.TextField; +import com.arsdigita.bebop.parameters.ParameterData; +import com.arsdigita.bebop.parameters.TrimmedStringParameter; +import com.arsdigita.bebop.parameters.NotEmptyValidationListener; +import com.arsdigita.bebop.util.GlobalizationUtil; +import org.libreccm.shortcuts.ShortcutManager; +import com.arsdigita.bebop.ParameterSingleSelectionModel; +import org.libreccm.shortcuts.Shortcut; +import org.libreccm.shortcuts.ShortcutRepository; +import com.arsdigita.util.UncheckedWrapperException; + +import org.apache.log4j.Category; +import org.apache.oro.text.perl.Perl5Util; +import org.apache.oro.text.perl.MalformedPerl5PatternException; +import org.libreccm.cdi.utils.CdiLookupException; +import org.libreccm.cdi.utils.CdiUtil; + +public class ShortcutForm extends Form { + + private static final Category log = Category.getInstance(ShortcutForm.class + .getName()); + + private ParameterSingleSelectionModel m_selected_shortcut; + + private TextField m_url; + + private TextField m_redirect; + + private final Submit m_submit; + + public ShortcutForm(ParameterSingleSelectionModel selected_shortcut) { + super("ShortcutForm"); + m_selected_shortcut = selected_shortcut; + + TrimmedStringParameter urlKeyParameter = new TrimmedStringParameter( + "url"); + urlKeyParameter.addParameterListener(new NotEmptyValidationListener()); + m_url = new TextField(urlKeyParameter); + + TrimmedStringParameter redirectParameter = new TrimmedStringParameter( + "redirect"); + redirectParameter + .addParameterListener(new NotEmptyValidationListener()); + + m_redirect = new TextField(redirectParameter); + + urlKeyParameter.addParameterListener(new ParameterListener() { + @Override + public void validate(ParameterEvent e) throws FormProcessException { + ParameterData data = e.getParameterData(); + + String key = (String) data.getValue(); + if (key == null) { + return; // Something else will handle + } // this + + Perl5Util perl = new Perl5Util(); + try { + if (!perl.match("/^(\\/[-a-zA-Z0-9_.]+)+\\/?$/", key)) { + data.addError(GlobalizationUtil.globalize( + "shortcuts.ui.invalid_key_descr")); + throw new FormProcessException( + "Invalid key", + GlobalizationUtil.globalize("shortcuts.ui.invalid_key") + ); + } + } catch (MalformedPerl5PatternException ex) { + throw new UncheckedWrapperException("bad regex", ex); + } + } + }); + + redirectParameter.addParameterListener(new ParameterListener() { + @Override + public void validate(ParameterEvent e) throws FormProcessException { + ParameterData data = e.getParameterData(); + + String url = (String) data.getValue(); + if (url == null) { + return; // Something else will handle + } // this + + url = url.toLowerCase(); + + // Absolute local url is ok + if (url.startsWith("/")) { + return; + } + + // Fully qualified url is ok + if (url.startsWith("http://")) { + return; + } + + // And secure ones too + if (url.startsWith("https://")) { + return; + } + + data + .addError("You must enter an absolute path " + + "(starting with '/') or a fully " + + "qualified URL (starting with 'http://' or 'https://')"); + throw new FormProcessException(GlobalizationUtil.globalize("shortcuts.ui.invalid_key")); + } + }); + + add(new Label("URL Key:")); + add(m_url); + add(new Label("Redirect:")); + add(m_redirect); + + m_submit = new Submit("Save Shortcut"); + add(m_submit); + + addInitListener(new ShortcutInitListener()); + addProcessListener(new ShortcutFormProcessListener()); + addValidationListener(new ShortcutFormValidationListener()); + } + + private class ShortcutInitListener implements FormInitListener { + + @Override + public void init(FormSectionEvent ev) throws FormProcessException { + final PageState state = ev.getPageState(); + final CdiUtil cdiUtil = new CdiUtil(); + final ShortcutRepository shortcutsRepo; + try { + shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); + } catch (CdiLookupException ex) { + throw new UncheckedWrapperException(ex); + } + Long shortcutKey = (Long) m_selected_shortcut + .getSelectedKey(state); + if (shortcutKey == null) { + log.debug("init form for empty shortcut"); + m_url.setValue(state, null); + m_redirect.setValue(state, null); + } else { + log.debug("init form for shortcut " + shortcutKey); +// Shortcut shortcut = new Shortcut(shortcutKey); + Shortcut shortcut = shortcutsRepo.findById(shortcutKey); + m_url.setValue(state, shortcut.getUrlKey()); + m_redirect.setValue(state, shortcut.getRedirect()); + } + } + } + + private class ShortcutFormValidationListener implements + FormValidationListener { + + @Override + public void validate(FormSectionEvent e) throws FormProcessException { + PageState state = e.getPageState(); + ShortcutManager shortcutMgr = new ShortcutManager(); + + // get currently edited shortcut + Long shortcutKey = (Long) m_selected_shortcut + .getSelectedKey(state); + +//TODO: maybe adjust url. see com-arsdigita.shortcuts.ShortcutUtil.cleanURLKey() + String key = (String) m_url.getValue(state); + + if (shortcutKey == null) { + String target = shortcutMgr.findByUrlKey(key).getUrlKey(); + + if (target != null) { + m_url.addError(GlobalizationUtil.globalize( + "shortcuts.ui.key_already_exists")); + throw new FormProcessException( + "duplicate key", + GlobalizationUtil.globalize("shortcuts.ui.duplicate_key") + ); + } + } + + int index = key.indexOf("/", 2); + String base = key.substring(0, index + 1); + } + } + + private class ShortcutFormProcessListener implements FormProcessListener { + + @Override + public void process(FormSectionEvent e) throws FormProcessException { + ShortcutManager shortcutMgr = new ShortcutManager(); + PageState state = e.getPageState(); + final CdiUtil cdiUtil = new CdiUtil(); + final ShortcutRepository shortcutsRepo; + try { + shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); + } catch (CdiLookupException ex) { + throw new UncheckedWrapperException(ex); + } + + Long shortcutKey = (Long) m_selected_shortcut.getSelectedKey(state); + +// String url = ShortcutUtil.cleanURLKey((String) m_url.getValue(state)); + String url = (String) m_url.getValue(state); + String redirect = (String) m_redirect.getValue(state); + + if (shortcutKey == null) { + log.info("save new shortcut " + url); + + Shortcut shortcut = shortcutMgr.createShortcut(url, redirect); + shortcutsRepo.save(shortcut); + } else { + log.info("save updated shortcut " + shortcutKey + " " + url); + + Shortcut shortcut = shortcutsRepo.findById(shortcutKey); + shortcut.setUrlKey(url); + shortcut.setRedirect(redirect); + shortcutsRepo.save(shortcut); + } + +// ShortcutUtil.repopulateShortcuts(); + + m_redirect.setValue(state, ""); + m_url.setValue(state, ""); + m_selected_shortcut.clearSelection(state); + } + } + +} diff --git a/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutsTable.java b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutsTable.java new file mode 100644 index 000000000..070e24bef --- /dev/null +++ b/ccm-shortcuts/src/main/java/org/libreccm/shortcuts/ui/ShortcutsTable.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 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 org.libreccm.shortcuts.ui; + +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.table.TableModelBuilder; +import com.arsdigita.util.LockableImpl; +import com.arsdigita.bebop.table.TableModel; +import com.arsdigita.bebop.PageState; + +import com.arsdigita.bebop.ParameterSingleSelectionModel; +import org.libreccm.shortcuts.Shortcut; +//import com.arsdigita.shortcuts.ShortcutCollection; +import java.math.BigDecimal; + +import org.apache.log4j.Category; + +import com.arsdigita.bebop.event.TableActionListener; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.event.TableActionEvent; +import com.arsdigita.bebop.ControlLink; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.bebop.Component; +import java.util.List; +import org.libreccm.cdi.utils.CdiLookupException; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.shortcuts.ShortcutRepository; + +/** + * + * + */ +public class ShortcutsTable extends Table { + + private static final Category log + = Category.getInstance(ShortcutsTable.class.getName()); + + public static final String headers[] = {"URL Key", "Redirect", "", ""}; + + public ShortcutsTable(final ParameterSingleSelectionModel selected_shortcut) { + super(new ShortcutsModelBuilder(), headers); + setDefaultCellRenderer(new ShortcutsCellRenderer()); + final CdiUtil cdiUtil = new CdiUtil(); + final ShortcutRepository shortcutsRepo; + try { + shortcutsRepo = cdiUtil.findBean(ShortcutRepository.class); + } catch (CdiLookupException ex) { + throw new UncheckedWrapperException(ex); + } + + addTableActionListener(new TableActionListener() { + public void cellSelected(TableActionEvent e) { + selected_shortcut.clearSelection(e.getPageState()); + String row = (String) e.getRowKey(); + if (e.getColumn().intValue() == 2) { + // edit selected + log.debug("selected edit shortcut " + row); + selected_shortcut.setSelectedKey(e.getPageState(), new BigDecimal(row)); + + } else if (e.getColumn().intValue() == 3) { + // delete selected + log.fatal("selected delete shortcut " + row); + + Shortcut shortcut = shortcutsRepo.findById( + (Long) selected_shortcut.getSelectedKey(e.getPageState())); + + if (shortcut != null) { + log.info("delete shortcut " + shortcut.getUrlKey()); + shortcutsRepo.delete(shortcut); + } + } + } + + public void headSelected(TableActionEvent e) { + } + }); + } + + protected static class ShortcutsModelBuilder extends LockableImpl implements + TableModelBuilder { + + public TableModel makeModel(Table table, PageState ps) { + return new ShortcutsModel(); + } + + protected class ShortcutsModel implements TableModel { + + private ShortcutRepository shortcutRepo = new ShortcutRepository(); + private List m_shortcuts = null; + private int index = 0; + private Shortcut m_shortcut; + + public ShortcutsModel() { +// m_shortcuts = Shortcut.retrieveAll(); + m_shortcuts = shortcutRepo.findAll(); + } + + public int getColumnCount() { + return headers.length; + } + + public boolean nextRow() { + if (index < m_shortcuts.size()) { + index++; + m_shortcut = m_shortcuts.get(index); + return true; + } else { + return false; + } + } + + public Object getElementAt(int col) { + return m_shortcut; + } + + public Long getKeyAt(int col) { + Long id = m_shortcut.getShortcutId(); + return id; + } + } + } + + protected static class ShortcutsCellRenderer implements TableCellRenderer { + + public Component getComponent(Table table, PageState state, + Object value, boolean isSelected, Object key, int row, + int column) { + Shortcut shortcut = (Shortcut) value; + + switch (column) { +// case 0: +// return new ExternalLink(shortcut.getUrlKey(), shortcut +// .getUrlKey()); +// case 1: +// return new ExternalLink(shortcut.getRedirect(), shortcut +// .getRedirect()); + case 2: + return new ControlLink(" edit "); + case 3: + return new ControlLink(" delete "); + default: + throw new UncheckedWrapperException("Column out of bounds"); + } + } + } +}