You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(48) |
Dec
(31) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(22) |
Feb
(68) |
Mar
(185) |
Apr
(11) |
May
(21) |
Jun
(23) |
Jul
(46) |
Aug
(69) |
Sep
(211) |
Oct
(26) |
Nov
(51) |
Dec
(52) |
2006 |
Jan
(13) |
Feb
(13) |
Mar
(8) |
Apr
(21) |
May
(17) |
Jun
(100) |
Jul
(34) |
Aug
(23) |
Sep
(26) |
Oct
(16) |
Nov
|
Dec
|
2007 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(66) |
Oct
(10) |
Nov
(1) |
Dec
|
2008 |
Jan
|
Feb
|
Mar
(1) |
Apr
(3) |
May
(8) |
Jun
(5) |
Jul
(31) |
Aug
(8) |
Sep
(11) |
Oct
(6) |
Nov
|
Dec
|
2012 |
Jan
(13) |
Feb
(2) |
Mar
(9) |
Apr
(6) |
May
(24) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(120) |
2013 |
Jan
(6) |
Feb
(35) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
Author: chrisg23 Date: 2007-09-17 14:00:29 +0200 (Mon, 17 Sep 2007) New Revision: 1638 Added: aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesSummary.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/AdditionalDisplayComponent.java Modified: aplaws/trunk/ccm-cms-assets-notes/pdl/com/arsdigita/london/notes/Notes.pdl aplaws/trunk/ccm-cms-assets-notes/src/WEB-INF/traversal-adapters/com/arsdigita/cms/contentassets/Notes.xml aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/Note.java aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/NotesInitializer.java aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesEdit.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/SimpleEditStep.java aplaws/trunk/ccm-core/src/com/arsdigita/bebop/PropertyEditor.java aplaws/trunk/ccm-core/src/com/arsdigita/bebop/Table.java aplaws/trunk/ccm-core/src/com/arsdigita/bebop/table/TableHeader.java Log: Sourceforge patch 1796156 - allow notes to be shown on basic properties authoring step Added: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/AdditionalDisplayComponent.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/AdditionalDisplayComponent.java (rev 0) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/AdditionalDisplayComponent.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2007 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.cms.ui.authoring; + +import com.arsdigita.bebop.Component; +import com.arsdigita.cms.ItemSelectionModel; + +/** + * class used for decoupled display components that caters + * for a callback to provide them with a handle on the ItemSelectionModel + * @author chr...@we... + * + */ +public interface AdditionalDisplayComponent extends Component { + + public void setItemSelectionModel (ItemSelectionModel model); + +} Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/SimpleEditStep.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/SimpleEditStep.java 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/SimpleEditStep.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -18,6 +18,11 @@ */ package com.arsdigita.cms.ui.authoring; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.arsdigita.bebop.Component; import com.arsdigita.bebop.Page; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.event.ActionEvent; @@ -63,7 +68,24 @@ private static final String STREAMLINED = "_streamlined"; private static final String STREAMLINED_DONE = "1"; + private static List s_additionalDisplayComponents = new ArrayList(); + /** + * allow additional display components to be added to all implementations + * of SimpleEditStep. This allows shared optional packages such as notes to + * display information on the initial authoring page of all content types without + * causing dependencies from ccm-cms. + * + * Any additional components must be added before the edit step is created. + * An initialiser is a suitable location + * + * @param c + */ + public static void addAdditionalDisplayComponent(AdditionalDisplayComponent c) { + s_additionalDisplayComponents.add(c); + } + + /** * Construct a new SimpleEditStep component * * @param itemModel The {@link ItemSelectionModel} which will @@ -104,6 +126,15 @@ showDisplayPane(state); } }); + + Iterator it = s_additionalDisplayComponents.iterator(); + while (it.hasNext()) { + + AdditionalDisplayComponent component = (AdditionalDisplayComponent)it.next(); + component.setItemSelectionModel(itemModel); + addDisplayComponent(component); + + } } /** Modified: aplaws/trunk/ccm-cms-assets-notes/pdl/com/arsdigita/london/notes/Notes.pdl =================================================================== --- aplaws/trunk/ccm-cms-assets-notes/pdl/com/arsdigita/london/notes/Notes.pdl 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-cms-assets-notes/pdl/com/arsdigita/london/notes/Notes.pdl 2007-09-17 12:00:29 UTC (rev 1638) @@ -16,12 +16,23 @@ import com.arsdigita.cms.ContentItem; import com.arsdigita.kernel.ACSObject; +import com.arsdigita.auditing.BasicAuditTrail; object type Note extends ACSObject { String[0..1] content = ca_notes.content CLOB; Long[1..1] rank = ca_notes.rank; reference key ( ca_notes.note_id ); + + // Basic auditing info + unversioned BasicAuditTrail[0..1] auditing = + qualias { filter(all(com.arsdigita.auditing.BasicAuditTrail), + id == this.id) }; + aggressive load(auditing.id, + auditing.creationDate, + auditing.creationIP, + auditing.lastModifiedDate, + auditing.lastModifiedIP); } association { Modified: aplaws/trunk/ccm-cms-assets-notes/src/WEB-INF/traversal-adapters/com/arsdigita/cms/contentassets/Notes.xml =================================================================== --- aplaws/trunk/ccm-cms-assets-notes/src/WEB-INF/traversal-adapters/com/arsdigita/cms/contentassets/Notes.xml 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-cms-assets-notes/src/WEB-INF/traversal-adapters/com/arsdigita/cms/contentassets/Notes.xml 2007-09-17 12:00:29 UTC (rev 1638) @@ -7,11 +7,15 @@ <!-- First off the adapters for ContentItemPanel --> <xrd:context name="com.arsdigita.cms.dispatcher.SimpleXMLGenerator"> <xrd:adapter objectType="com.arsdigita.london.notes.Note"> - <xrd:attributes rule="exclude"> + <!-- <xrd:attributes rule="exclude"> <xrd:property name="/object/defaultDomainClass"/> <xrd:property name="/object/objectType"/> <xrd:property name="/object/displayName"/> </xrd:attributes> + +--> + <xrd:attributes rule="include"> + </xrd:attributes> </xrd:adapter> </xrd:context> @@ -19,12 +23,15 @@ <!-- Adapter for search --> <xrd:context name="com.arsdigita.cms.search.ContentPageMetadataProvider"> <xrd:adapter objectType="com.arsdigita.london.notes.Note"> - <xrd:attributes rule="exclude"> + <!-- <xrd:attributes rule="exclude"> <xrd:property name="/object/id"/> <xrd:property name="/object/defaultDomainClass"/> <xrd:property name="/object/displayName"/> <xrd:property name="/object/rank"/> + </xrd:attributes> --> + <xrd:attributes rule="include"> </xrd:attributes> + </xrd:adapter> </xrd:context> Modified: aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/Note.java =================================================================== --- aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/Note.java 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/Note.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -16,11 +16,16 @@ package com.arsdigita.london.notes; +import java.util.Date; + +import com.arsdigita.auditing.AuditingObserver; +import com.arsdigita.auditing.BasicAuditTrail; import com.arsdigita.cms.ContentItem; import com.arsdigita.domain.DomainObject; import com.arsdigita.domain.DomainObjectFactory; import com.arsdigita.domain.DomainObjectInstantiator; import com.arsdigita.kernel.ACSObject; +import com.arsdigita.kernel.User; import com.arsdigita.kernel.permissions.PermissionService; import com.arsdigita.persistence.DataCollection; import com.arsdigita.persistence.DataObject; @@ -55,7 +60,12 @@ public static final String RANK = "rank"; public static final String OWNER = "owner"; public static final String NOTES = "ca_notes"; + public static final String AUDIT = "auditing"; + public static final String CREATION_DATE = AUDIT + "." + BasicAuditTrail.CREATION_DATE; + + private BasicAuditTrail auditTrail; + private boolean m_isNew = false; private Note() { @@ -81,6 +91,25 @@ return note; } + /** + * Register auditing observer + * (non-Javadoc) + * @see com.arsdigita.domain.DomainObject#initialize() + */ + protected void initialize() { + super.initialize(); + + DataObject dataObj = (DataObject) get(AUDIT); + if (dataObj != null) { + auditTrail = new BasicAuditTrail(dataObj); + } else { + // creates a new one when one doesn't already exist + auditTrail = BasicAuditTrail.retrieveForACSObject(this); + } + + addObserver(new AuditingObserver(auditTrail)); + } + public String getContent() { return (String) get( CONTENT ); } @@ -131,6 +160,14 @@ return (ContentItem) DomainObjectFactory.newInstance( obj ); } + public User getNoteAuthor () { + return auditTrail.getCreationUser(); + } + + public Date getCreationDate () { + return auditTrail.getCreationDate(); + } + public static DataCollection getNotes( ContentItem item ) { Assert.exists( item, ContentItem.class ); Modified: aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/NotesInitializer.java =================================================================== --- aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/NotesInitializer.java 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/NotesInitializer.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -16,17 +16,18 @@ package com.arsdigita.london.notes; -import com.arsdigita.cms.contenttypes.ContentAssetInitializer; import com.arsdigita.cms.ContentPage; import com.arsdigita.cms.ContentType; +import com.arsdigita.cms.contenttypes.ContentAssetInitializer; import com.arsdigita.cms.dispatcher.SimpleXMLGenerator; +import com.arsdigita.cms.ui.authoring.SimpleEditStep; import com.arsdigita.domain.DomainObjectTraversal; import com.arsdigita.domain.SimpleDomainObjectTraversalAdapter; import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.london.notes.ui.NotesStep; +import com.arsdigita.london.notes.ui.NotesSummary; import com.arsdigita.runtime.LegacyInitEvent; -import com.arsdigita.london.notes.ui.NotesStep; - public class NotesInitializer extends ContentAssetInitializer { public NotesInitializer() { super( "ccm-cms-assets-notes.pdl.mf" ); @@ -73,5 +74,6 @@ DomainObjectTraversal.registerAdapter( Note.BASE_DATA_OBJECT_TYPE, new SimpleDomainObjectTraversalAdapter(), SimpleXMLGenerator.ADAPTER_CONTEXT ); + SimpleEditStep.addAdditionalDisplayComponent(new NotesSummary()); } } Modified: aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesEdit.java =================================================================== --- aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesEdit.java 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesEdit.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -29,6 +29,7 @@ import com.arsdigita.bebop.parameters.NotNullValidationListener; import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ContentSection; import com.arsdigita.kernel.ui.ACSObjectSelectionModel; import com.arsdigita.london.notes.Note; @@ -53,7 +54,7 @@ StringParameter contentParam = new StringParameter( "content" ); contentParam.addParameterListener( new NotNullValidationListener() ); - final DHTMLEditor content = new DHTMLEditor( contentParam ); + final DHTMLEditor content = new DHTMLEditor( contentParam,ContentSection.getConfig().getDHTMLEditorConfig() ); content.setRows( 20 ); m_form.add( content ); Added: aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesSummary.java =================================================================== --- aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesSummary.java (rev 0) +++ aplaws/trunk/ccm-cms-assets-notes/src/com/arsdigita/london/notes/ui/NotesSummary.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -0,0 +1,135 @@ +package com.arsdigita.london.notes.ui; + +import java.text.DateFormat; +import java.util.Date; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Table; +import com.arsdigita.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableColumn; +import com.arsdigita.bebop.table.TableColumnModel; +import com.arsdigita.bebop.table.TableModel; +import com.arsdigita.bebop.table.TableModelBuilder; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.ui.authoring.AdditionalDisplayComponent; +import com.arsdigita.domain.DomainObjectFactory; +import com.arsdigita.kernel.User; +import com.arsdigita.london.notes.Note; +import com.arsdigita.persistence.DataCollection; +import com.arsdigita.util.LockableImpl; + +public class NotesSummary extends Table implements AdditionalDisplayComponent { + + private ItemSelectionModel m_itemSelectionModel; + + public NotesSummary() { + super(); + setModelBuilder(new NotesTableBuilder()); + TableColumnModel model = getColumnModel(); + model.add( new TableColumn( 0, "Note" )); + model.add( new TableColumn( 1, "Date" )); + model.add( new TableColumn( 2, "By" )); + setRowSelectionModel(null); + setColumnSelectionModel(null); + model.get(0).setCellRenderer(new TableCellRenderer() { + + public Component getComponent(Table table, PageState state, Object value, boolean isSelected, Object key, int row, int column) { + Label t = new Label((String)value); + t.setOutputEscaping(false); + + return t; + } + + + }); + + + + + } + + public void setItemSelectionModel(ItemSelectionModel model) { + m_itemSelectionModel = model; + } + + private class NotesTableBuilder extends LockableImpl implements TableModelBuilder { + + + public TableModel makeModel(Table t, PageState state) { + return new NotesTableModel(m_itemSelectionModel.getSelectedItem(state)); + + } + + + + } + + + private class NotesTableModel implements TableModel { + + private DataCollection m_notes = null; + private Note m_currentNote; + + public NotesTableModel (ContentItem item) { + if (item != null) { + m_notes = Note.getNotes(item); + // cg already ordered by rank m_notes.addOrder(Note.CREATION_DATE + " desc"); + } + } + public int getColumnCount() { + return 3; + } + + public Object getElementAt(int columnIndex) { + switch (columnIndex) { + case 0: + return m_currentNote.getContent(); + + + case 1: + String displayDate = "Not recorded"; + Date creationDate = m_currentNote.getCreationDate(); + if (creationDate != null) { + displayDate = DateFormat.getDateInstance(DateFormat.MEDIUM).format(creationDate); + } + return displayDate; + + + case 2: + String displayAuthor = "Not recorded"; + User author = m_currentNote.getNoteAuthor(); + if (author != null) { + displayAuthor = author.getName(); + } + return displayAuthor; + + default : + throw new IndexOutOfBoundsException( + "Column index " + columnIndex + " not in table model."); + } + } + + public Object getKeyAt(int columnIndex) { + return m_currentNote.getID(); + } + + public boolean nextRow () { + if ( m_notes.next() ) { + m_currentNote = (Note)DomainObjectFactory.newInstance(m_notes.getDataObject()); + return true; + } else { + m_notes.close(); + return false; + } + + } + + } + +} + + + Modified: aplaws/trunk/ccm-core/src/com/arsdigita/bebop/PropertyEditor.java =================================================================== --- aplaws/trunk/ccm-core/src/com/arsdigita/bebop/PropertyEditor.java 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-core/src/com/arsdigita/bebop/PropertyEditor.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -35,6 +35,7 @@ import com.arsdigita.bebop.form.FormErrorDisplay; import com.arsdigita.bebop.form.Submit; +import java.util.ArrayList; import java.util.Iterator; import java.util.Map; @@ -150,6 +151,7 @@ private List m_list; private PropertyEditorModelBuilder m_builder; private RequestLocal m_model; + private java.util.List m_additionalDisplayComponents = new ArrayList(); /** * Constructs a new, empty <code>PropertyEditor</code>. @@ -277,6 +279,13 @@ } /** + * add an additional component below the list of links + * @param c + */ + public void addDisplayComponent(Component c) { + m_additionalDisplayComponents.add(c); + } + /** * Adds the display component if it has not been added already. * * @param c the display component to add @@ -288,6 +297,10 @@ m_displayPane.add(c); m_displayPane.add(m_list); + Iterator it = m_additionalDisplayComponents.iterator(); + while (it.hasNext()) { + m_displayPane.add((Component)it.next()); + } m_display = c; } Modified: aplaws/trunk/ccm-core/src/com/arsdigita/bebop/Table.java =================================================================== --- aplaws/trunk/ccm-core/src/com/arsdigita/bebop/Table.java 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-core/src/com/arsdigita/bebop/Table.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -552,11 +552,11 @@ * @param p the page that contains this table */ public void register(Page p) { - ParameterModel m = getRowSelectionModel().getStateParameter(); + ParameterModel m = getRowSelectionModel() == null ? null : getRowSelectionModel().getStateParameter(); if ( m != null ) { p.addComponentStateParam(this, m); } - m = getColumnSelectionModel().getStateParameter(); + m = getColumnSelectionModel() == null ? null : getColumnSelectionModel().getStateParameter(); if ( m != null ) { p.addComponentStateParam(this, m); } @@ -601,7 +601,7 @@ * <code>false</code> otherwise. */ public boolean isSelectedRow(PageState s, Object rowKey) { - if ( rowKey == null ) { + if ( rowKey == null || getRowSelectionModel() == null) { return false; } return getRowSelectionModel().isSelected(s) @@ -619,7 +619,7 @@ * <code>false</code> otherwise. */ public boolean isSelectedColumn(PageState s, Object column) { - if ( column == null ) { + if ( column == null || getColumnSelectionModel() == null) { return false; } return getColumnSelectionModel().isSelected(s) Modified: aplaws/trunk/ccm-core/src/com/arsdigita/bebop/table/TableHeader.java =================================================================== --- aplaws/trunk/ccm-core/src/com/arsdigita/bebop/table/TableHeader.java 2007-09-17 10:14:27 UTC (rev 1637) +++ aplaws/trunk/ccm-core/src/com/arsdigita/bebop/table/TableHeader.java 2007-09-17 12:00:29 UTC (rev 1638) @@ -283,6 +283,9 @@ * @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) { |
Author: chrisg23 Date: 2007-09-17 13:00:28 +0200 (Mon, 17 Sep 2007) New Revision: 1637 Added: aplaws/trunk/ccm-cms/sql/ccm-cms/default/upgrade/6.5.2-6.5.3/ aplaws/trunk/ccm-cms/sql/ccm-cms/default/upgrade/6.5.2-6.5.3/cms_task_url_generators_upgrade.sql aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/oracle-se-6.5.2-6.5.3.sql aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/postgres-6.5.2-6.5.3.sql Modified: aplaws/trunk/ccm-cms/application.xml aplaws/trunk/ccm-cms/pdl/com/arsdigita/content-section/CMSTask.pdl aplaws/trunk/ccm-cms/src/ccm-cms.upgrade aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTask.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTaskType.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/TaskEventURLGenerator.java Log: Sourceforge patch 1796099 - allow URL Generators to be registered for specific content types. Requires upgrade script to be run: ccm-run ccm-cms --from-version 6.5.2 --to-version 6.5.3 Modified: aplaws/trunk/ccm-cms/application.xml =================================================================== --- aplaws/trunk/ccm-cms/application.xml 2007-09-17 09:59:41 UTC (rev 1636) +++ aplaws/trunk/ccm-cms/application.xml 2007-09-17 11:00:28 UTC (rev 1637) @@ -2,11 +2,11 @@ <ccm:application xmlns:ccm="http://ccm.redhat.com/ccm-project" name="ccm-cms" prettyName="Red Hat CCM Content Management System" - version="6.5.2" - release="4" + version="6.5.3" + release="1" webapp="ROOT"> <ccm:dependencies> - <ccm:requires name="ccm-core" version="6.5.2" relation="ge"/> + <ccm:requires name="ccm-core" version="6.5.5" relation="ge"/> </ccm:dependencies> <ccm:contacts> <ccm:contact uri="http://www.redhat.com/software/ccm" type="website"/> Modified: aplaws/trunk/ccm-cms/pdl/com/arsdigita/content-section/CMSTask.pdl =================================================================== --- aplaws/trunk/ccm-cms/pdl/com/arsdigita/content-section/CMSTask.pdl 2007-09-17 09:59:41 UTC (rev 1636) +++ aplaws/trunk/ccm-cms/pdl/com/arsdigita/content-section/CMSTask.pdl 2007-09-17 11:00:28 UTC (rev 1637) @@ -16,6 +16,7 @@ import com.arsdigita.workflow.simple.*; import com.arsdigita.kernel.ACSObject; +import com.arsdigita.cms.ContentType; object type CMSTask extends UserTask { composite CMSTaskType [1..1] taskType = join cms_tasks.task_type_id to cms_task_types.task_type_id; @@ -36,6 +37,7 @@ object type TaskEventURLGenerator { Integer [1..1] generatorID = cms_task_url_generators.generator_id INTEGER; String [1..1] event = cms_task_url_generators.event VARCHAR(100); + ContentType [0..1] contentType = join cms_task_url_generators.content_type to content_types.type_id; String [1..1] urlGeneratorClass = cms_task_url_generators.classname VARCHAR(128); object key (generatorID); Added: aplaws/trunk/ccm-cms/sql/ccm-cms/default/upgrade/6.5.2-6.5.3/cms_task_url_generators_upgrade.sql =================================================================== --- aplaws/trunk/ccm-cms/sql/ccm-cms/default/upgrade/6.5.2-6.5.3/cms_task_url_generators_upgrade.sql (rev 0) +++ aplaws/trunk/ccm-cms/sql/ccm-cms/default/upgrade/6.5.2-6.5.3/cms_task_url_generators_upgrade.sql 2007-09-17 11:00:28 UTC (rev 1637) @@ -0,0 +1,6 @@ +alter table CMS_TASK_URL_GENERATORS +add (content_type INTEGER); + +alter table cms_task_url_generators add + constraint cms_tas_url_gen_con_ty_f_lz1y5 foreign key (content_type) + references content_types(type_id); Added: aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/oracle-se-6.5.2-6.5.3.sql =================================================================== --- aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/oracle-se-6.5.2-6.5.3.sql (rev 0) +++ aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/oracle-se-6.5.2-6.5.3.sql 2007-09-17 11:00:28 UTC (rev 1637) @@ -0,0 +1,24 @@ +-- +-- Copyright (C) 2007 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 +-- +-- $Id: oracle-se-6.5.2-6.5.3.sql 293 2005-02-22 15:10:39Z cgilbert $ +-- $DateTime: 2004/08/16 18:10:38 $ + +PROMPT Red Hat Enterprise CMS 6.5.2 -> 6.5.3 Upgrade Script (Oracle) + +@@ ../default/upgrade/6.5.2-6.5.3/cms_task_url_generators_upgrade.sql + Added: aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/postgres-6.5.2-6.5.3.sql =================================================================== --- aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/postgres-6.5.2-6.5.3.sql (rev 0) +++ aplaws/trunk/ccm-cms/sql/ccm-cms/upgrade/postgres-6.5.2-6.5.3.sql 2007-09-17 11:00:28 UTC (rev 1637) @@ -0,0 +1,26 @@ +-- +-- Copyright (C) 2007 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 +-- +-- $DateTime: 2004/08/17 23:15:09 $ + +\echo Red Hat Enterprise CMS 6.5.2 -> 6.5.3 Upgrade Script (PostgreSQL) + +begin; + +\i ../default/upgrade/6.5.2-6.5.3/cms_task_url_generators_upgrade.sql + +commit; Modified: aplaws/trunk/ccm-cms/src/ccm-cms.upgrade =================================================================== --- aplaws/trunk/ccm-cms/src/ccm-cms.upgrade 2007-09-17 09:59:41 UTC (rev 1636) +++ aplaws/trunk/ccm-cms/src/ccm-cms.upgrade 2007-09-17 11:00:28 UTC (rev 1637) @@ -29,5 +29,8 @@ <version from="6.5.0" to="6.5.1"> <script sql="ccm-cms/upgrade/::database::-6.5.0-6.5.1.sql"/> </version> + <version from="6.5.2" to="6.5.3"> + <script sql="ccm-cms/upgrade/::database::-6.5.2-6.5.3.sql"/> + </version> </upgrade> Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTask.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTask.java 2007-09-17 09:59:41 UTC (rev 1636) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTask.java 2007-09-17 11:00:28 UTC (rev 1637) @@ -255,13 +255,15 @@ Assert.assertNotNull(item, "item associated with this CMSTask"); String authoringURL = getAuthoringURL(item); - String fullURL = URL.there(getTaskType().getURLGenerator(operation).generateURL(item.getID(), getID()), null).getURL(); + String fullURL = getTaskType().getURLGenerator(operation, item).generateURL(item.getID(), getID()); + s_log.debug("URL retrieved from generator: " + fullURL); + if (!fullURL.startsWith("http")) { + // url is not fully qualified + fullURL = URL.there(fullURL, null).getURL(); + + } // see CMSResources.properties for how these values are used - Object[] g11nArgs = new Object[10]; - // cg - make this configurable. Because our content section managers have access to all - // folders, they get all notifications, but they want to know which area it relates to - - // first folder in item path tells them. config parameter - full item path or just display name - // g11nArgs[0] =((ContentItem)item.getParent()).getPath(); + Object[] g11nArgs = new Object[11]; g11nArgs[0] = item.getDisplayName(); g11nArgs[1] = new Double(getTaskType().getID().doubleValue()); g11nArgs[2] = fullURL; @@ -283,7 +285,9 @@ } g11nArgs[8] = getStartDate(); g11nArgs[9] = URL.there(authoringURL, null).getURL(); - + //if added to email, allows recipient to identify if the item is in a folder + // they are interested in + g11nArgs[10] = ((ContentItem)item.getParent()).getPath(); String subject = (String) GlobalizationUtil.globalize("cms.ui.workflow.email.subject." + operation, g11nArgs).localize(); String body = (String) GlobalizationUtil.globalize("cms.ui.workflow.email.body." + operation, @@ -481,8 +485,10 @@ } } } + // bugfix - if author is null above then we break out + // of loop early. If normal exit and so cursor has already closed then the + // next line has no effect hist.close(); - //hist.close here if not closed if (author == null) { // fallback: creator is always available in audit trail author = item.getCreationUser(); Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTaskType.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTaskType.java 2007-09-17 09:59:41 UTC (rev 1636) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/CMSTaskType.java 2007-09-17 11:00:28 UTC (rev 1637) @@ -20,6 +20,7 @@ import com.arsdigita.cms.ContentItem; import com.arsdigita.cms.ContentSection; +import com.arsdigita.cms.ContentType; import com.arsdigita.cms.SecurityManager; import com.arsdigita.cms.ui.ContentItemPage; import com.arsdigita.cms.util.GlobalizationUtil; @@ -168,23 +169,36 @@ } - public TaskURLGenerator getURLGenerator(String event) { - String key = getID() + " " + event; + public TaskURLGenerator getURLGenerator(String event, ContentItem item) { + String key = getID() + " " + event + " " + item.getContentType().getID(); s_log.debug("looking up url generator for key " + key); TaskURLGenerator generator = (TaskURLGenerator) s_taskURLGeneratorCache.get(key); if (generator == null) { - s_log.debug("generator not found in cache"); + s_log.debug("no generator found in cache"); DataAssociationCursor generators = ((DataAssociation)get(URL_GENERATORS)).cursor(); generators.addEqualsFilter(TaskEventURLGenerator.EVENT, event); + generators.addEqualsFilter(TaskEventURLGenerator.CONTENT_TYPE + "." + ContentType.ID, item.getContentType().getID()); try { while (generators.next()) { - s_log.debug("specific generator found for " + event + " event on task type " + getName()); + s_log.debug("specific generator found for " + event + " event on task type " + getName() + " for content type " + item.getContentType().getLabel()); + // generator class available for this specific event and this specific content type + generator = ((TaskEventURLGenerator)DomainObjectFactory.newInstance(generators.getDataObject())).getGenerator(); + generators.close(); + } + if (generator == null) { + generators.reset(); + generators.addEqualsFilter(TaskEventURLGenerator.EVENT, event); + generators.addEqualsFilter(TaskEventURLGenerator.CONTENT_TYPE, null); + while (generators.next()) { + s_log.debug("specific generator found for " + event + " event on task type " + getName() + " for any content type"); // generator class available for this specific event generator = ((TaskEventURLGenerator)DomainObjectFactory.newInstance(generators.getDataObject())).getGenerator(); generators.close(); } + } + if (generator == null) { s_log.debug("no specific generator for " + event + " event on task type " + getName()+ ". Revert to default"); Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/TaskEventURLGenerator.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/TaskEventURLGenerator.java 2007-09-17 09:59:41 UTC (rev 1636) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/workflow/TaskEventURLGenerator.java 2007-09-17 11:00:28 UTC (rev 1637) @@ -18,51 +18,14 @@ */ package com.arsdigita.cms.workflow; -import com.arsdigita.cms.ContentItem; -import com.arsdigita.cms.ContentSection; -import com.arsdigita.cms.SecurityManager; -import com.arsdigita.cms.ui.ContentItemPage; -import com.arsdigita.cms.util.GlobalizationUtil; -import com.arsdigita.domain.DataObjectNotFoundException; +import org.apache.log4j.Logger; + import com.arsdigita.domain.DomainObject; import com.arsdigita.domain.DomainObjectFactory; -import com.arsdigita.kernel.Group; -import com.arsdigita.kernel.KernelHelper; -import com.arsdigita.kernel.Party; -import com.arsdigita.kernel.User; -import com.arsdigita.kernel.UserCollection; -import com.arsdigita.kernel.permissions.PermissionService; -import com.arsdigita.kernel.permissions.PrivilegeDescriptor; -import com.arsdigita.messaging.Message; -import com.arsdigita.notification.Notification; -import com.arsdigita.persistence.DataAssociation; -import com.arsdigita.persistence.DataAssociationCursor; import com.arsdigita.persistence.DataCollection; import com.arsdigita.persistence.DataObject; -import com.arsdigita.persistence.DataOperation; -import com.arsdigita.persistence.DataQuery; -import com.arsdigita.persistence.Filter; import com.arsdigita.persistence.OID; -import com.arsdigita.persistence.Session; import com.arsdigita.persistence.SessionManager; -import com.arsdigita.persistence.metadata.ObjectType; -import com.arsdigita.util.Assert; -import com.arsdigita.versioning.TagCollection; -import com.arsdigita.versioning.Transaction; -import com.arsdigita.versioning.TransactionCollection; -import com.arsdigita.versioning.Versions; -import com.arsdigita.web.URL; -import com.arsdigita.workflow.simple.TaskComment; -import com.arsdigita.workflow.simple.TaskException; -import com.arsdigita.workflow.simple.UserTask; -import org.apache.log4j.Logger; -import java.math.BigDecimal; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; /** * This class represents enables fine grained control of the url that is @@ -87,6 +50,7 @@ public static final String EVENT = "event"; public static final String URL_GENERATOR_CLASS = "urlGeneratorClass"; + public static final String CONTENT_TYPE = "contentType"; private static final Logger s_log = Logger.getLogger(TaskEventURLGenerator.class); |
Author: chrisg23 Date: 2007-09-17 12:00:44 +0200 (Mon, 17 Sep 2007) New Revision: 1636 Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/application.xml aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/ aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/ aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/gov/ aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/gov/westsussex/portlet/ aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/gov/westsussex/portlet/NewsPortlet.pdl aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/ aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/oracle-se-create.sql aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/postgres-create.sql aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.config aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.load aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Initializer.java aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader.java aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader_parameter.properties aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsConstants.java aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortlet.java aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig.java aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig_parameter.properties aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/PersonalisedNewsTarget.java aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletEditor.java aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletRenderer.java aplaws/contrib/wsx/ccm-wsx-news-portlet/web/ aplaws/contrib/wsx/ccm-wsx-news-portlet/web/STATIC/ aplaws/contrib/wsx/ccm-wsx-news-portlet/web/STATIC/portlet/ aplaws/contrib/wsx/ccm-wsx-news-portlet/web/STATIC/portlet/news_arrow.gif aplaws/contrib/wsx/ccm-wsx-news-portlet/web/packages/ aplaws/contrib/wsx/ccm-wsx-news-portlet/web/packages/westsussex-portlets/ aplaws/contrib/wsx/ccm-wsx-news-portlet/web/packages/westsussex-portlets/xsl/ aplaws/contrib/wsx/ccm-wsx-news-portlet/web/packages/westsussex-portlets/xsl/news-portlet.xsl Log: News Portlet that sources articles from rss feed by default, but allows implementations of personalised news (currently the Atomwide module retrieves personalised news for school users if authors assign articles to terms within the schools and roles domains managed by the atomwide module) Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/application.xml =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/application.xml (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/application.xml 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<ccm:application name="ccm-wsx-news-portlet" + prettyName="Personalised News Portlet" + version="1.0.1" + release="1" + webapp="ROOT" + xmlns:ccm="http://ccm.redhat.com/ccm-project"> + + <ccm:dependencies> + <ccm:requires name="ccm-ldn-portal" version="1.4.2"/> + <ccm:requires name="ccm-cms-types-newsitem" version="6.1.0"/> + <ccm:requires name="ccm-wsx-authentication" version="1.0.1"/> + + + + </ccm:dependencies> + + + <ccm:contacts> + <ccm:contact uri="http://wsgfl.westsussex.gov.uk" type="website"/> + <ccm:contact uri="mailto:chr...@we..." type="support"/> + </ccm:contacts> + + <ccm:description> + Portlet either displays the latest news content, or the latest personalised items + </ccm:description> +</ccm:application> Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/gov/westsussex/portlet/NewsPortlet.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/gov/westsussex/portlet/NewsPortlet.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/pdl/uk/gov/westsussex/portlet/NewsPortlet.pdl 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,12 @@ +// author chris gilbert +// model for a portlet that displays the latest news + +model uk.gov.westsussex.portlet; + +import com.arsdigita.portal.Portlet; + +object type NewsPortlet extends Portlet { + Integer[0..1] itemCount = portlet_news.item_count INTEGER; + + reference key (portlet_news.portlet_id); +} Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/oracle-se-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/oracle-se-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/oracle-se-create.sql 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,2 @@ +@ ddl/oracle-se/create.sql +@ ddl/oracle-se/deferred.sql Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/postgres-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/postgres-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/sql/ccm-wsx-news-portlet/postgres-create.sql 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,4 @@ +begin; +\i ddl/postgres/create.sql +\i ddl/postgres/deferred.sql +end; Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.config =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.config (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.config 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<registry> + <config class="uk.gov.westsussex.portlet.news.NewsPortletConfig" + storage="ccm-wsx-news-portlet/newsportlet.properties"/> +</registry> Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.load =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.load (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/ccm-wsx-news-portlet.load 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,22 @@ +<load> + <requires> + <table name="inits"/> + <table name="acs_objects"/> + <initializer class="com.arsdigita.core.Initializer"/> + <table name="cms_items"/> + <initializer class="com.arsdigita.cms.Initializer"/> + <initializer class="com.arsdigita.london.portal.Initializer"/> + <initializer class="uk.gov.westsussex.authentication.Initializer"/> + <initializer class="com.arsdigita.cms.contenttypes.NewsItemInitializer"/> + <initializer class="com.arsdigita.london.navigation.Initializer"/> + + </requires> + <provides> + <table name="portlet_news"/> + <initializer class="uk.gov.westsussex.portlet.news.Initializer"/> + </provides> + <scripts> + <schema directory="ccm-wsx-news-portlet"/> + <data class="uk.gov.westsussex.portlet.news.Loader"/> + </scripts> +</load> Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Initializer.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Initializer.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Initializer.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,80 @@ +package uk.gov.westsussex.portlet.news; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.portlet.news.ui.NewsPortletEditor; + +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.db.DbHelper; +import com.arsdigita.domain.DomainObject; +import com.arsdigita.kernel.ACSObjectInstantiator; +import com.arsdigita.kernel.ResourceType; +import com.arsdigita.kernel.ResourceTypeConfig; +import com.arsdigita.kernel.ui.ResourceConfigFormSection; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.pdl.ManifestSource; +import com.arsdigita.persistence.pdl.NameFilter; +import com.arsdigita.portal.PortletType; +import com.arsdigita.runtime.CompoundInitializer; +import com.arsdigita.runtime.DomainInitEvent; +import com.arsdigita.runtime.PDLInitializer; +import com.arsdigita.runtime.RuntimeConfig; + +/** + * based on com.arsdigita.london.portal.installer.portlet + */ +public class Initializer extends CompoundInitializer { + public final static String versionId = + "$Id: Initializer.java,v 1.2 2005/03/07 13:48:49 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/02 06:33:42 $"; + + private static final Logger s_log = Logger.getLogger(Initializer.class); + + public Initializer() { + final String url = RuntimeConfig.getConfig().getJDBCURL(); + final int database = DbHelper.getDatabaseFromURL(url); + + add( + new PDLInitializer( + new ManifestSource( + "ccm-wsx-news-portlet.pdl.mf", + new NameFilter( + DbHelper.getDatabaseSuffix(database), + "pdl")))); + } + + public void init(DomainInitEvent e) { + super.init(e); + + e + .getFactory() + .registerInstantiator( + NewsPortlet.BASE_DATA_OBJECT_TYPE, + new ACSObjectInstantiator() { + public DomainObject doNewInstance(DataObject dataObject) { + return new NewsPortlet(dataObject); + } + }); + + new ResourceTypeConfig(NewsPortlet.BASE_DATA_OBJECT_TYPE) { + public ResourceConfigFormSection getCreateFormSection( + final ResourceType resType, + final RequestLocal parentAppRL) { + final ResourceConfigFormSection config = + new NewsPortletEditor(resType, parentAppRL); + + return config; + } + + public ResourceConfigFormSection getModifyFormSection(final RequestLocal application) { + final NewsPortletEditor config = + new NewsPortletEditor(application); + + return config; + } + }; + PortletType.registerXSLFile(NewsPortlet.BASE_DATA_OBJECT_TYPE, "/packages/westsussex-portlets/xsl/news-portlet.xsl"); + + } +} \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,45 @@ +package uk.gov.westsussex.portlet.news; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.Kernel; +import com.arsdigita.kernel.KernelExcursion; +import com.arsdigita.loader.PackageLoader; +import com.arsdigita.portal.PortletType; +import com.arsdigita.runtime.ScriptContext; +import com.arsdigita.util.parameter.Parameter; +import com.arsdigita.util.parameter.StringParameter; + + + +public class Loader extends PackageLoader { + public final static String versionId = + "$Id: Loader.java,v 1.1 2005/02/28 16:28:49 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/02 06:33:42 $"; + + private static final Logger s_log = Logger.getLogger(Loader.class); + + private StringParameter typeName = new StringParameter + ("uk.gov.westsussex.portlet.news.name", + Parameter.REQUIRED, "News"); + + public Loader() { + register(typeName); + + } + public void run(final ScriptContext ctx) { + new KernelExcursion() { + public void excurse() { + setEffectiveParty(Kernel.getSystemParty()); + PortletType type = PortletType + .createPortletType((String)get(typeName), + PortletType.WIDE_PROFILE, + NewsPortlet.BASE_DATA_OBJECT_TYPE); + type.setDescription("See your personalised news"); + } + }.run(); + } + + +} Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader_parameter.properties =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader_parameter.properties (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/Loader_parameter.properties 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,5 @@ +uk.gov.westsussex.portlet.news.name.title=Portlet Type Name +uk.gov.westsussex.portlet.news.name.purpose=The name of the portlet type that appears in the drop down list of portlet types +uk.gov.westsussex.portlet.news.name.example=News +uk.gov.westsussex.portlet.news.name.format=[string] + Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsConstants.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsConstants.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsConstants.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,52 @@ +/* + * Created on 10-Sep-04 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.portlet.news; + +/** + * @author cgyg9330 + * + * Constants used by classes in the bookmarksportlet application. + * + */ +// to do - globalised text + +public interface NewsConstants { + + + + public static final String ITEM_COUNT = "itemCount"; + + + + public static final String GENERAL_NEWS_CACHE_KEY = "generalNews"; + + //queries + public static final String RECENT_NEWS = + "uk.gov.westsussex.portal.portlet.RecentNews"; + public static final String PERSONALISED_NEWS = + "uk.gov.westsussex.portal.portlet.PersonalisedNews"; + public static final String LAST_UPDATE = + "uk.gov.westsussex.portal.portlet.LatestNewsDate"; + + + + + + // rendering + + public static final String XML_NEWS_NS = "http://wsgfl.westsussex.gov.uk/portlet/news/1.0"; + public static final String MAIN_PORTLET_ELEMENT = "portlet:news"; + public static final String NEWS_ITEM_ELEMENT = "news-portlet:newsItem"; + public static final String NEWS_ROOM_ATTRIBUTE = "newsroom-shortcut"; + public static final String PERSONALISED_ATTRIBUTE = "personalised"; + public static final String DATE_ATTRIBUTE = "date"; + public static final String TITLE_ATTRIBUTE = "title"; + public static final String LEAD_ATTRIBUTE = "lead"; + public static final String URL_ATTRIBUTE = "url"; + + +} Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortlet.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortlet.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortlet.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,102 @@ +/* + * Created on 24-Oct-03 + * + * + */ +package uk.gov.westsussex.portlet.news; + +import java.math.BigDecimal; +import java.util.Date; + +import uk.gov.westsussex.portlet.news.ui.NewsPortletRenderer; + +import com.arsdigita.bebop.portal.AbstractPortletRenderer; +import com.arsdigita.cms.contenttypes.NewsItem; +import com.arsdigita.persistence.DataCollection; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.portal.Portlet; + + +/** + * @author cgyg9330 + * + * List of news content types items, in descending order of date. The number + * of items retrieved is set by the administrator. + */ +public class NewsPortlet extends Portlet implements NewsConstants { + + + private static NewsPortletConfig s_config = new NewsPortletConfig(); + + static { + s_config.load(); + } + + public static NewsPortletConfig getConfig() { + return s_config; + } + + /** + * allows non personalised news page to be cached between updates + */ + private static BigDecimal s_latestNews = new BigDecimal(0); + private static long s_newsCount = 0; + + public NewsPortlet(DataObject dataObject) { + super(dataObject); + } + + public static final String BASE_DATA_OBJECT_TYPE = + "uk.gov.westsussex.portlet.NewsPortlet"; + + + protected String getBaseDataObjectType() { + return BASE_DATA_OBJECT_TYPE; + } + + protected AbstractPortletRenderer doGetPortletRenderer() { + return new NewsPortletRenderer(this); + } + + public int getItemCount() { + return ((Integer) get(ITEM_COUNT)).intValue(); + } + + public void setItemCount(int count) { + set(ITEM_COUNT, new Integer(count)); + } + + /** + * + * @return whether any homepage news items have been added or edited since last checked. + * If news has been updated, the stored value of the last update is changed. Note + * deleted news items do not cause true to be returned. + * + */ + /* + * + * starting to look doubtful whether this would actually save any time + * + * will recheck when more data on database + public boolean isNewNews() { + NewsItem latest = NewsItem.getMostRecentNewsItem(); + + if (!latest.getID().equals(s_latestNews)) { + s_latestNews = latest.getID(); + return true; + } + DataCollection news = SessionManager.getSession().retrieve(NewsItem.BASE_DATA_OBJECT_TYPE); + news.addEqualsFilter(NewsItem.IS_HOMEPAGE, new Boolean(true)); + long newsCount = news.size(); + if (newsCount != s_newsCount) { + s_newsCount = newsCount; + return true; + } + return false; + + + + }*/ + +} Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.portlet.news; + +import com.arsdigita.runtime.AbstractConfig; +import com.arsdigita.util.parameter.IntegerParameter; +import com.arsdigita.util.parameter.Parameter; +import com.arsdigita.util.parameter.StringArrayParameter; +import com.arsdigita.util.parameter.URLParameter; +import com.arsdigita.util.parameter.StringParameter; +import com.arsdigita.util.UncheckedWrapperException; + +import com.arsdigita.persistence.Session; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.kernel.UserCollection; +import com.arsdigita.kernel.User; +import com.arsdigita.web.Web; + +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; +import java.net.MalformedURLException; +import org.apache.log4j.Logger; + +/** + * Specification of datasources required by Atomwide application + * + * @author Chris Gilbert <chr...@we...> + * @version $Id: NewsPortletConfig.java,v 1.1 2005/03/07 13:48:49 cgyg9330 Exp $ + */ +public class NewsPortletConfig extends AbstractConfig { + public final static String versionId = + "$Id: NewsPortletConfig.java,v 1.1 2005/03/07 13:48:49 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/18 11:10:20 $"; + + private static final Logger s_log = Logger.getLogger(NewsPortletConfig.class); + + + + + private StringParameter newsroomShortcut = + new StringParameter( + "uk.gov.westsussex.portlet.news.newsroom-shortcut", + Parameter.REQUIRED, + "/news"); + + + public NewsPortletConfig() { + + register(newsroomShortcut); + loadInfo(); + } + + + public final String getNewsroomShortcut() { + return (String)get(newsroomShortcut); + } + +} Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig_parameter.properties =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig_parameter.properties (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/NewsPortletConfig_parameter.properties 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,4 @@ +uk.gov.westsussex.portlet.news.newsroom-shortcut.title=Newsroom Shortcut +uk.gov.westsussex.portlet.news.newsroom-shortcut.purpose=used as a link at the bottom of the news portlet +uk.gov.westsussex.portlet.news.newsroom-shortcut.example=/news +uk.gov.westsussex.portlet.news.newsroom-shortcut.format=[string] Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/PersonalisedNewsTarget.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/PersonalisedNewsTarget.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/PersonalisedNewsTarget.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,33 @@ +/* + * Created on 28-Feb-05 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.portlet.news; + +import java.util.List; + +import com.arsdigita.domain.DomainCollection; + +/** + * @author chr...@we... + * + * Service is used by news portlet to display potentially + * useful articles to the user. + * + * An implementation will require a mapping between content pages and + * some attributes of the user. In the atomwide authentication module, user profile values + * are modelled as terms in domains mapped to /content/ and so authors may specify + * which user groups their articles are to be promoted to + */ + +public interface PersonalisedNewsTarget { + /** + * + * @return a domainCollection of com.arsdigita.cms.ContentPage objects + */ + + public DomainCollection getMyNews(); + +} Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletEditor.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletEditor.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletEditor.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,78 @@ +/* + * Created on 24-Oct-03 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.portlet.news.ui; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.portlet.news.NewsPortlet; + +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.form.TextField; +import com.arsdigita.bebop.parameters.IntegerValidationListener; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.bebop.portal.PortletConfigFormSection; +import com.arsdigita.kernel.ResourceType; +import com.arsdigita.portal.Portlet; + +/** + * @author jcop6130 + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class NewsPortletEditor extends PortletConfigFormSection { + private TextField m_itemCount; + private static final Logger s_log = Logger.getLogger(NewsPortletEditor.class); + + public NewsPortletEditor(ResourceType resType, RequestLocal parentAppRL) { + super(resType, parentAppRL); + } + + public NewsPortletEditor(RequestLocal application) { + super(application); + } + + protected void addWidgets() { + super.addWidgets(); + + m_itemCount = + new TextField(new StringParameter(NewsPortlet.ITEM_COUNT)); + + add(new Label("Number of items:", Label.BOLD), ColumnPanel.RIGHT); + + m_itemCount.addValidationListener(new IntegerValidationListener()); + + add(m_itemCount); + + } + // add validation or set up drop down list with contents of Static folder? + protected void initWidgets(PageState state, Portlet portlet) + throws FormProcessException { + super.initWidgets(state, portlet); + + if (portlet != null) { + NewsPortlet myportlet = (NewsPortlet) portlet; + + m_itemCount.setValue(state, myportlet.getItemCount()+ ""); + } + } + + protected void processWidgets(PageState state, Portlet portlet) + throws FormProcessException { + s_log.debug("START processWidgets"); + super.processWidgets(state, portlet); + + NewsPortlet myportlet = (NewsPortlet) portlet; + myportlet.setItemCount(new Integer( + (String) m_itemCount.getValue(state)).intValue()); + s_log.debug("END processWidgets"); + } +} Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletRenderer.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletRenderer.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/src/uk/gov/westsussex/portlet/news/ui/NewsPortletRenderer.java 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,221 @@ +/* + * Created on 24-Oct-03 + * + */ +package uk.gov.westsussex.portlet.news.ui; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.authentication.ExternalUserFactory; +import uk.gov.westsussex.portlet.news.NewsConstants; +import uk.gov.westsussex.portlet.news.NewsPortlet; +import uk.gov.westsussex.portlet.news.PersonalisedNewsTarget; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.portal.AbstractPortletRenderer; +import com.arsdigita.categorization.Category; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ContentPage; +import com.arsdigita.cms.SecurityManager; +import com.arsdigita.domain.DomainCollection; +import com.arsdigita.domain.DomainObjectFactory; +import com.arsdigita.kernel.Kernel; +import com.arsdigita.kernel.User; +import com.arsdigita.kernel.permissions.PermissionService; +import com.arsdigita.kernel.permissions.PrivilegeDescriptor; +import com.arsdigita.london.navigation.Navigation; +import com.arsdigita.london.portal.ui.PortalConstants; +import com.arsdigita.london.terms.Domain; +import com.arsdigita.london.terms.Term; +import com.arsdigita.persistence.DataCollection; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.xml.Element; + +/** + * @author cgyg9330 + * + */ +public class NewsPortletRenderer + extends AbstractPortletRenderer + implements NewsConstants { + + private static final Logger s_log = + Logger.getLogger(NewsPortletRenderer.class); + + public NewsPortletRenderer(NewsPortlet portlet) { + m_portlet = portlet; + } + + private NewsPortlet m_portlet; + + /* (non-Javadoc) + * @see com.arsdigita.bebop.portal.AbstractPortletRenderer#generateBodyXML(com.arsdigita.bebop.PageState, com.arsdigita.xml.Element) + */ + protected void generateBodyXML(PageState state, Element parent) { + s_log.debug("START - generateBodyXML"); + Element newsPortlet = + parent.newChildElement( + MAIN_PORTLET_ELEMENT, + PortalConstants.PORTLET_XML_NS); + DateFormat formatter = new SimpleDateFormat("dd/MM/yy"); + User thisUser = (User) Kernel.getContext().getParty(); + if (null == thisUser) { + thisUser = Kernel.getPublicUser(); + } + Object customUser = ExternalUserFactory.getCustomUserObject(thisUser); + if (customUser instanceof PersonalisedNewsTarget) { + newsPortlet.addAttribute(PERSONALISED_ATTRIBUTE, "true"); + getPersonalisedNews( + state, + newsPortlet, + (PersonalisedNewsTarget) customUser, + formatter); + + } else { + newsPortlet.addAttribute(PERSONALISED_ATTRIBUTE, "false"); + getGeneralNews(newsPortlet, formatter); + + } + + newsPortlet.addAttribute( + NEWS_ROOM_ATTRIBUTE, + NewsPortlet.getConfig().getNewsroomShortcut()); + s_log.debug("FINISH - generateBodyXML"); + } + + /** + * @param newsPortlet + * @param profiledUser + */ + private void getPersonalisedNews( + PageState state, + Element main, + PersonalisedNewsTarget profiledUser, + DateFormat formatter) { + s_log.debug("START - getPersonalisedNews"); + DomainCollection myNews = profiledUser.getMyNews(); + if (myNews == null) { + // method in personalised user class has been implemented to just return null + getGeneralNews(main, formatter); + return; + } + + myNews.addOrder(ContentPage.LAUNCH_DATE + " desc"); + + PermissionService.filterObjects( + myNews, + PrivilegeDescriptor.get(SecurityManager.CMS_READ_ITEM), + Kernel.getContext().getParty().getOID()); + + myNews.setRange( + new Integer(1), + new Integer(m_portlet.getItemCount() + 1)); + + while (myNews.next()) { + ContentPage page = (ContentPage) myNews.getDomainObject(); + + Element item = main.newChildElement(NEWS_ITEM_ELEMENT, XML_NEWS_NS); + Date d = page.getLaunchDate(); + String date = (d != null) ? formatter.format(d) : ""; + item.addAttribute(DATE_ATTRIBUTE, date); + + item.addAttribute(TITLE_ATTRIBUTE, page.getTitle()); + item.addAttribute(LEAD_ATTRIBUTE, page.getSearchSummary()); + item.addAttribute( + URL_ATTRIBUTE, + Navigation.redirectURL(page.getOID())); + + } + s_log.debug("END - getPersonalisedNews"); + + } + private void getGeneralNews(Element main, DateFormat formatter) { + s_log.debug("START - getGeneralNews"); + // this is the default key - maybe should parametrise + Domain rss = Domain.retrieve("APLAWS-RSS"); + DomainCollection rssRoots = rss.getRootTerms(); + rssRoots.addEqualsFilter(Term.NAME, "News"); + Term newsTerm = null; + while (rssRoots.next()) { + newsTerm = (Term) rssRoots.getDomainObject(); + s_log.debug("found the news rss feed term"); + Category cat = newsTerm.getModel(); + DataCollection newsItems = SessionManager.getSession().retrieve(ContentPage.BASE_DATA_OBJECT_TYPE); + newsItems.addEqualsFilter("parent.categories.id", + cat.getID()); + + newsItems.addEqualsFilter(ContentItem.VERSION, ContentItem.LIVE); + newsItems.addOrder(ContentPage.LAUNCH_DATE + " desc"); + + + + + + + + // CategorizedCollection newsItems = + // cat.getObjects(ContentPage.BASE_DATA_OBJECT_TYPE); + s_log.debug("total items = " + newsItems.size()); + newsItems.setRange( + new Integer(1), + new Integer(m_portlet.getItemCount() + 1)); + + ContentPage newsItem = null; + while (newsItems.next()) { + Element item = + main.newChildElement(NEWS_ITEM_ELEMENT, XML_NEWS_NS); + //newsItem = (ContentPage) newsItems.getDomainObject(); + newsItem = (ContentPage)DomainObjectFactory.newInstance(newsItems.getDataObject()); + Date d = newsItem.getLaunchDate(); + String date = (d != null) ? formatter.format(d) : ""; + item.addAttribute(DATE_ATTRIBUTE, date); + + item.addAttribute(TITLE_ATTRIBUTE, newsItem.getTitle()); + + item.addAttribute(LEAD_ATTRIBUTE, newsItem.getSearchSummary()); + item.addAttribute( + URL_ATTRIBUTE, + Navigation.redirectURL(newsItem.getOID())); + + } + } + + s_log.debug("END - getGeneralNews"); + } + /* + public Object getCacheKey() { + + if (getProfiledUser() == null) { + return GENERAL_NEWS_CACHE_KEY; + } else { + + + return m_portlet.getID(); + } + } + + // is dirty if edited, as this means the number of entries has changed. For non personalised news, dirty if homepage + // newsitems are added or edited. For personalised, dirty if pushed items added or edited. + + public boolean isDirty() { + + + // has it been edited? - add in this condition when response from Redhat about AbstractPortletRenderer + + // if not has news changed + if (getCacheKey().equals(GENERAL_NEWS_CACHE_KEY)) { + isDirty = m_portlet.isNewNews(); + s_log.debug("general news: dirty? " + isDirty); + + } else { + // implement later + isDirty = true; + } + } + return isDirty; + */ + } Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/web/STATIC/portlet/news_arrow.gif =================================================================== (Binary files differ) Property changes on: aplaws/contrib/wsx/ccm-wsx-news-portlet/web/STATIC/portlet/news_arrow.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/contrib/wsx/ccm-wsx-news-portlet/web/packages/westsussex-portlets/xsl/news-portlet.xsl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-news-portlet/web/packages/westsussex-portlets/xsl/news-portlet.xsl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-news-portlet/web/packages/westsussex-portlets/xsl/news-portlet.xsl 2007-09-17 10:00:44 UTC (rev 1636) @@ -0,0 +1,72 @@ +<?xml version="1.0"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:news-portlet="http://wsgfl.westsussex.gov.uk/portlet/news/1.0" xmlns:portlet="http://www.uk.arsdigita.com/portlet/1.0" xmlns:bebop="http://www.arsdigita.com/bebop/1.0"> + + <xsl:template match="portlet:news"> + <xsl:if test="@personalised= 'true'"> + <table width="100%" cellpadding="0" cellspacing="0" border="0"> + <tr> + <td width="3"> + <img alt="" src="/STATIC/portlet/spacer.gif" width="100%" /> + </td> + <td class="portletText">Latest articles for you...</td> + </tr> + </table> + </xsl:if> + + <table width="100%" cellpadding="0" cellspacing="0" border="0"> + <xsl:apply-templates /> + <tr> + <td colspan="3" width="100%" height="7"> + <img alt="" src="/STATIC/portlet/spacer.gif" width="100%" height="3" /> + </td> + </tr> + </table> + <table width="100%" cellpadding="0" cellspacing="0" border="0"> + <tr> + + <td height="20" width="" valign="middle" class="portletText"> + <br/> + <img alt = "" src="/STATIC/portlet/spacer.gif" width="8" /> + <a href="{@newsroom-shortcut}">News Room</a> + <img alt="" src="/STATIC/portlet/spacer.gif" width="10" height="1" /> + <a href="{@newsroom-shortcut}"> + <img src="/STATIC/portlet/news_arrow.gif" alt="More News" /> + </a> + </td> + </tr> + </table> + + </xsl:template> + + <xsl:template match="news-portlet:newsItem"> + <tr> + <td colspan="3" width="100%" height="7"> + <img alt="" src="/STATIC/portlet/spacer.gif" width="100%" height="3" /> + </td> + </tr> + <tr> + <td height="20" width="" valign="middle" class="portletText"> + <img alt = "" src="/STATIC/portlet/arrow_bullet.gif" /> + </td> + <td height="20" width="423" align="left" valign="middle" class="portletText"> + <a> + <xsl:attribute name="href"> + <xsl:value-of select="@url" /> + </xsl:attribute> + <xsl:value-of select="@title" /> + </a> + - + <xsl:value-of select="@date" /> + </td> + <td height="20" valign="middle" width="2" class="portletText"></td> + </tr> + <tr> + <td></td> + <td width="423" align="left" valign="middle" class="portletText"> + <xsl:value-of select="@lead" /> + </td> + </tr> + + </xsl:template> + +</xsl:stylesheet> \ No newline at end of file |
Author: chrisg23 Date: 2007-09-17 12:00:38 +0200 (Mon, 17 Sep 2007) New Revision: 1635 Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/application.xml aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/TermDate.pdl aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/navigation/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/navigation/Category.pdl aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/query-missing-notification-phases.pdl aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.config aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.load aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.upgrade aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CookieManager.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CredentialLoginModule.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/UserLoginModule.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepagePortalSelectionModel.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspace.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspaceSelectionModel.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/cms/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/cms/workflow/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/cms/workflow/DeployTaskURLGenerator.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/CategorySpecificNavigationModel.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/Initializer.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/TermDate.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/UnpublishNotification.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/Util.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/WSGfLConfig.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/WSGfLConfig_parameter.properties aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/WSGfLCustom.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/WSGfLResourceBundle.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/WSGfLResources.properties aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/data/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/data/DatabaseConnection.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/CEDAgencyInformationProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/CEDEstablishmentInformationProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/CEDSchoolInformationProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/CEDUnitInformationProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/DominoSchoolInformationProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/EducationEstablishment.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/EstablishmentInformationAdapter.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/EstablishmentInformationProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/EstablishmentListener.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/data/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/data/CEDEstablishmentDAO.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/establishment/data/CEDEstablishmentData.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/AddTermDates.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/ConvertJiveForum.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/CreateMissingNotificationPhases.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/RSSPortletLoad.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/RemoveExcessTasks.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/RemoveWorkflows.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/jobs/SendTestEmails.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/navigation/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/navigation/Menu.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/navigation/NavigationFileResolver.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/navigation/Path.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/navigation/PopulatedSubcategoryTreeCatProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/navigation/SkipLevelTreeCatProvider.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/login/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/login/DynamicLink.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/login/LoginHelper.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/login/SubsiteDispatcher.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/login/SubsiteResources_en.properties aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/login/SubsiteResources_fr.properties aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/wsgfl/ui/login/UserRegistrationForm.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/westsussex/wsgfl/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/westsussex/wsgfl/UtilitiesTest.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/westsussex/wsgfl/WSGfLCaseTestSetup.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/westsussex/wsgfl/WSGfLTestCaseSuite.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/westsussex/wsgfl/utilities/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/test/src/uk/gov/westsussex/wsgfl/utilities/HttpUnitTestCase.java aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/WEB-INF/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/WEB-INF/web.xml.wsgfl aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/favicon.ico aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/javascript/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/javascript/CalendarPopup.js aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/javascript/emergency-contacts.js aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/javascript/lmdu.js aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/content-section/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/content-section/templates/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/content-section/templates/default/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/content-section/templates/default/non-cached-aplaws-item.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/wsx/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/wsx/templates/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/wsx/templates/access-controlled-navigation.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/wsx/templates/access-controlled-navigation.jsp.orig aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/packages/wsx/templates/access-controlled-portal-navigation.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/robots.txt aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/portal/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/portal/admin/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/portal/admin/index.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/portal/admin/sitemap.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/portal/edit.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/portal/index.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/templates/ccm-wsx-portal/portal/workspaces.jsp aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/wsgfl-assets/ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/web/wsgfl-assets/redirect.htm Log: Custom Code for West Sussex Grid for Learning - includes generic Estabishment lookup used by other applications Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/application.xml =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/application.xml (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/application.xml 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<ccm:application name="ccm-wsx-wsgfl-custom" + prettyName="WSGfL Custom Code" + version="1.0.2" + release="1" + webapp="ROOT" + xmlns:ccm="http://ccm.redhat.com/ccm-project"> + + <ccm:dependencies> + <ccm:requires name="ccm-ldn-portal" version="1.4.2"/> + </ccm:dependencies> + <ccm:directories> + <ccm:directory name="pdl"/> + <ccm:directory name="web"/> + <ccm:directory name="src"/> + </ccm:directories> + + <ccm:contacts> + <ccm:contact uri="http://www.redhat.com/software/rhea" type="website"/> + <ccm:contact uri="mailto:rh...@re..." type="support"/> + </ccm:contacts> + + <ccm:description> + Code that is specific to the WSGfL + </ccm:description> + +</ccm:application> Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/TermDate.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/TermDate.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/TermDate.pdl 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,14 @@ +model uk.gov.westsussex; + +import com.arsdigita.kernel.ACSObject; + + +object type TermDate extends ACSObject { + Date [1..1] startDate = term_dates.start_date TIMESTAMP; + Date[1..1] endDate = term_dates.end_date TIMESTAMP; + String [1..1] academicYear = term_dates.academic_year VARCHAR(255); + String [1..1] termName = term_dates.term_name VARCHAR(255); + + reference key (term_dates.term_id); + unique(academicYear, termName); +} Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/navigation/Category.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/navigation/Category.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/navigation/Category.pdl 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,101 @@ + +model uk.gov.westsussex.navigation; + +import com.arsdigita.kernel.ACSObject; +import com.arsdigita.categorization.Category; + +query PopulatedSubcategories { + ACSObject objectInSubCat; + Category subCat; + options { + WRAP_QUERIES = false; + } + + do { + + select a.object_id, + a.object_type, + a.display_name, + a.default_domain_class, + c.category_id, + c.description, + c.name, + c.url, + c.enabled_p, + c.abstract_p, + c.default_ancestors + from acs_objects a, + acs_objects b, + cat_categories c, + cat_object_category_map d, + cat_category_category_map e, + cms_items f + where e.category_id = :categoryID + and c.category_id = e.related_category_id + and b.object_id = c.category_id + and d.category_id = e.related_category_id + and e.relation_type = 'child' + and a.object_id = d.object_id + and f.item_id(+) = a.object_id + and nvl(f.version, 'live') = 'live' + } map { + objectInSubCat.id = a.object_id; + objectInSubCat.objectType = a.object_type; + objectInSubCat.displayName = a.display_name; + objectInSubCat.defaultDomainClass = a.default_domain_class; + subCat.id = b.object_id; + subCat.objectType = b.object_type; + subCat.displayName = b.display_name; + subCat.defaultDomainClass = b.default_domain_class; + subCat.description = c.description; + subCat.name = c.name; + subCat.url = c.url; + subCat.isEnabled = c.enabled_p; + subCat.isAbstract = c.abstract_p; + subCat.defaultAncestors = c.default_ancestors; + + + } +} + + +// copied from Atomwide application - used here to find groups for role groupings during initialisation +// config specifies role groupings, we need to find groups associated both with that role grouping, and the 'all schools' term +// use for in subqueries eg +// select * from groups g +// where g.group_id in (select object_id from CAT_OBJECT_CATEGORY_MAP where category_id = 21385) +// and g.group_id in (select object_id from CAT_OBJECT_CATEGORY_MAP where category_id = 22701) +// to find the group categorised under a particular school AND a particular role grouping +query ObjectsForCategory { + BigDecimal objectID; + options { + WRAP_QUERIES = false; + } + + do { + select object_id + from CAT_OBJECT_CATEGORY_MAP + where category_id = :catID + } map { + objectID = object_id; + } +} + + +// oh dear - if I try to do the above, the persistance layer cannot distinguish between +// the 2 different bind variables called catID, so I need to have 2 queries +query ObjectsForCategory2 { + BigDecimal objectID; + options { + WRAP_QUERIES = false; + } + + do { + select object_id + from CAT_OBJECT_CATEGORY_MAP + where category_id = :catID2 + } map { + objectID = object_id; + } +} + Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/query-missing-notification-phases.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/query-missing-notification-phases.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/pdl/uk/gov/westsussex/query-missing-notification-phases.pdl 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,27 @@ + +model uk.gov.westsussex.wsgfl; + +query DeficientCycles { + BigDecimal cycleID; + options { + WRAP_QUERIES = false; + } + + do { + + select a.cycle_id from lifecycles a, + cms_pages b, + acs_object_lifecycle_map c + where a.cycle_id = c.cycle_id + and b.item_id = c.item_id + and a.end_date_time is not null + and not exists (select 1 from phases x, phase_definitions y + where x.cycle_id = a.cycle_id + and x.definition_id = y.phase_definition_id + and y.label = 'expirationImminent') + + + } map { + cycleID = a.cycle_id; + } +} \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.config =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.config (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.config 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<registry> + <config class="uk.gov.westsussex.wsgfl.WSGfLConfig" + storage="ccm-wsx-wsgfl-custom/wsgfl.properties"/> +</registry> Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.load =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.load (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.load 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,11 @@ +<load> + <requires> + <table name="inits"/> + <table name="acs_objects"/> + <initializer class="com.arsdigita.core.Initializer"/> + + </requires> + <provides> + <initializer class="uk.gov.westsussex.wsgfl.Initializer"/> + </provides> +</load> Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.upgrade =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.upgrade (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/ccm-wsx-wsgfl-custom.upgrade 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,6 @@ +<upgrade> + <version from="1.0.1" to="1.0.2"> + <script sql="ccm-wsx-wsgfl-custom/upgrade/::database::-1.0.1-1.0.2.sql"/> + </version> + +</upgrade> Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CookieManager.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CookieManager.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CookieManager.java 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,198 @@ +/* + * 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.kernel.security; + +import com.arsdigita.kernel.Kernel; +import com.arsdigita.util.ServletUtils; +import com.arsdigita.util.UncheckedWrapperException; + +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import javax.servlet.http.Cookie; + +import org.apache.log4j.Logger; + +/** + * Manages a string value stored in a cookie. + * + * @see CookieLoginModule + * + * @author Sameer Ajmani + **/ +public class CookieManager extends CredentialManager { + + public static final String versionId = "$Id: CookieManager.java 1477 2007-03-14 10:27:16Z chrisgilbert23 $ by $Author: chrisgilbert23 $, $DateTime: 2004/08/16 18:10:38 $"; + private static final Logger s_log = + Logger.getLogger(CookieManager.class.getName()); + + public void initialize(CredentialLoginModule module, + Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + super.initialize(module, subject, handler, shared, options); + } + + /** + * Determines whether <code>setValue()</code> should be called. + * + * @param value the new value for the credential + * + * @return <code>true</code> if the credential is not set or has the + * wrong value or should be renewed, <code>false</code> otherwise. + **/ + protected boolean shouldSetValue(String value) + throws LoginException { + if (getModule().requestIsExcluded()) { + return false; + } + return !getModule().credentialIsSet() + || !getModule().credentialHasValue(value) + || getModule().credentialIsOld(); + } + + /** + * Returns the value of the cookie named + * <code>getModule().getCredentialName()</code>. + * + * @return the value of the cookie named + * <code>getModule().getCredentialName()</code>. + * + * @throws CredentialNotFoundException if the cookie is not in the + * current request. + * + * @throws LoginException if an error occurs. + **/ + protected final String getValue() + throws LoginException { + s_log.debug("START getValue"); + String value = ServletUtils.getCookieValue + (getModule().getRequest(), + getModule().getCredentialName()); + if (value == null) { + s_log.debug("FAILURE getValue"); + throw new CredentialNotFoundException(); + } + s_log.debug("SUCCESS getValue: "+value); + return value; + } + + /** + * Sets the cookie named <code>getModule().getCredentialName()</code> to + * the given value. + * + * @throws LoginException if an error occurs. + **/ + protected final void setValue(String value) + throws LoginException { + // now we don't automatically set the duration to getCookieMaxAge() + // setCookie(getModule().getCredentialName(), value, getCookieAge()); + // yes we do - cookie age was correctly set to either forever, or + // -1 (exists for duration of browser session). The config parameter + // for cookie duration needs to apply to the credential timestamp + // in order for correct behaviour - This change has been applied + // in UserLoginModule - chr...@we... + setCookie(getModule().getCredentialName(), value, getCookieMaxAge()); + } + + /** + * Deletes the cookie named + * <code>getModule().getCredentialName()</code>. + * + * @throws LoginException if an error occurs. + **/ + protected final void deleteValue() + throws LoginException { + deleteCookie(getModule().getCredentialName()); + } + + /** + * Deletes the named cookie. + **/ + private void deleteCookie(String name) + throws LoginException { + if (isCookieSet(name)) { + s_log.debug("deleting existing cookie"); + setCookie(name, "", 0); // maxAge == 0 deletes cookie + } else { + s_log.debug("Not deleting cookie since it doesn't exist!"); + } + } + + private boolean isCookieSet(String name) { + Cookie cookies[] = null; + try { + cookies = getModule().getRequest().getCookies(); + } catch (LoginException ex) { + throw new UncheckedWrapperException(ex); + } + + if (cookies == null) + return false; + + for (int i = 0 ; i < cookies.length ; i++) + if (cookies[i].getName().equals(name)) + return true; + + return false; + } + + /** + * Sets the named cookie to the given value. + **/ + private void setCookie(String name, String value, int maxAge) + throws LoginException { + Cookie cookie = new Cookie(name, value); + cookie.setMaxAge(maxAge); + cookie.setPath("/"); +// cookie.setSecure(getModule().isSecure()); + cookie.setSecure(false); + getModule().getResponse().addCookie(cookie); + String domain = Kernel.getSecurityConfig().getCookieDomain(); + if (domain != null) { + cookie.setDomain(domain); + } + s_log.debug("Cookie set: domain - " + cookie.getDomain() + + " name - " + cookie.getName()); + } + + /** + * Determines the lifespan of the cookie, using the setting + * of the configuration, defaulting to getCookieMaxAge(). + **/ + protected int getCookieAge() throws LoginException { + Integer setting = Kernel.getSecurityConfig().getCookieDurationMinutes(); + return (setting == null ? getCookieMaxAge() : setting.intValue() * 60); + } + + /** + * Determines the correct max age for the cookie in seconds. A return + * value of -1 means the cookie should be deleted when the client's + * browser quits. + * + * @return <code>FOREVER_SECS</code> if the user has requested permanent + * login; -1 otherwise. + **/ + protected int getCookieMaxAge() throws LoginException { + return getModule().getForever() ? + (int)CredentialLoginModule.FOREVER_SECS : -1; + } +} Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CredentialLoginModule.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CredentialLoginModule.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/CredentialLoginModule.java 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,475 @@ +/* + * 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.kernel.security; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.Kernel; + +/** + * Loads an ID from a credential stored in the current HTTP request (for + * example, cookie or URL parameter). If the credential is invalid but the + * ID is loaded successfully using another <code>LoginModule</code>, this + * module sets a new credential containing the ID. This class uses the + * <i>Strategy</i> design pattern to manage the persistent credential value; + * it delegates to the <code>CredentialManager</code> provided at + * construction to get, set, and delete the credential value. + * + * @see CredentialManager + * + * @author Sameer Ajmani + **/ +public abstract class CredentialLoginModule implements LoginModule { + + public static final String versionId = "$Id: CredentialLoginModule.java 718 2005-08-18 15:34:42Z apevec $ by $Author: apevec $, $DateTime: 2004/08/16 18:10:38 $"; + private static final Logger s_log = + Logger.getLogger(CredentialLoginModule.class.getName()); + + /** + * Minimum time allowed between session renewals, in seconds. + **/ + public static final long RENEW_SECS = 60*5; // 5 mins + + /** + * Maximum time allowed between clicks in a single session, in seconds. + **/ + public static final long TIMEOUT_SECS = 60*20; // 20 mins + + /** + * Maximum time that a single session can last, in seconds. + **/ + public static final long LIFETIME_SECS = 60*60*24*2; // 2 days + + /** + * Maximum time that a "permanent" credential can last, in seconds. + **/ + public static final long FOREVER_SECS = 60*60*24*365*3; // 3 years + + // fields set by initialize() + private Subject m_subject; + private CallbackHandler m_handler; + private Map m_shared; + private Map m_options; + + // cached values for accessors + private HttpServletRequest m_req = null; + private HttpServletResponse m_res = null; + private Boolean m_forever = null; + private Boolean m_secure = null; + + // the credential itself + private Credential m_credential = null; + + // manages the credential value + private CredentialManager m_manager; + + /** + * Creates a new <code>CredentialLoginModule</code> associated with the + * given <code>CredentialManager</code>. This module uses the given + * manager to get, set, and delete the credential value. + **/ + public CredentialLoginModule(CredentialManager manager) { + m_manager = manager; + } + + /** + * Initializes this login module and its <code>CredentialManager</code> + * with the given login context information. This method is called by + * <code>LoginContext</code> after this class is instantiated. + **/ + public void initialize(Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + m_manager.initialize(this, subject, handler, shared, options); + m_subject = subject; + m_handler = handler; + m_shared = shared; + m_options = options; + } + + /** + * Checks whether the current request contains a valid credential. + * + * @return <code>true<code>. + * @throws CredentialNotFoundException if the credential is missing. + * @throws CredentialParsingException if the credential is invalid. + * @throws CredentialExpiredException if the credential has expired. + * @throws LoginException if an error occurs. + **/ + public boolean login() throws LoginException { + s_log.debug("START login"); + loadCredential(); + s_log.debug("END login"); + return true; + } + + /** + * Deletes the credential and invalidates the client session. + * + * @return <code>true</code>. + * @throws LoginException if an error occurs. + **/ + public boolean logout() throws LoginException { + s_log.debug("START logout"); + invalidateSession(); + deleteCredential(); + s_log.debug("END logout"); + return true; + } + + /** + * Deletes the credential. + * + * @return true + * @throws LoginException if an error occurs + **/ + public boolean abort() throws LoginException { + // overall login failed + s_log.debug("START abort"); + deleteCredential(); + s_log.debug("END abort"); + return true; + } + + /** + * Sets the ID for the Subject to the value of the credential if no ID + * is already set. If needed, updates the credential. If the Subject + * has an ID different from the one in the credential, invalidates the + * client session. + * + * @return <code>true</code>. + * @throws FailedLoginException if unable to set ID. + * @throws LoginException if an error occurs. + **/ + public boolean commit() throws LoginException { + // overall login succeeded + s_log.debug("START commit"); + try { + BigDecimal id = getID(); + String value = id.toString(); + if (shouldInvalidateSession(value)) { + invalidateSession(); + } + if (m_manager.shouldSetValue(value)) { + setCredential(value); + } + s_log.debug("SUCCESS commit"); + } catch (FailedLoginException e) { + // ignore failure, because another LoginModule may succeed + s_log.debug("FAILURE commit (ignored)", e); + } + return true; + } + + /** + * Reads the ID from the Subject and sets it if needed. First, checks + * if some other LoginModule has assigned an ID to the Subject. If not, + * reads the ID from the credential and sets it in the Subject. + * + * @return the ID. + * @throws FailedLoginException if the ID is not available. + **/ + private BigDecimal getID() + throws FailedLoginException { + s_log.debug("START getID"); + try { + // load ID from subject + BigDecimal id = loadID(); + s_log.debug("SUCCESS getID from subject"); + return id; + } catch (NoSuchElementException e) { + // load ID from credential + if (!credentialIsSet()) { + s_log.debug("FAILURE getID not available", e); + throw new FailedLoginException("No ID available"); + } + try { + String value = m_credential.getValue(); + BigDecimal id = new BigDecimal(value); + s_log.debug("SUCCESS getID from credential"); + saveID(id); + return id; + } catch (NumberFormatException nfe) { + s_log.debug("FAILURE getID number format", nfe); + throw new FailedLoginException("No ID available"); + } + } + } + + /** + * Loads the ID from the Subject. + **/ + protected final BigDecimal loadID() + throws NoSuchElementException { + Set set = m_subject.getPrincipals(PartyPrincipal.class); + return ((PartyPrincipal)set.iterator().next()).getID(); + } + + /** + * Saves the given ID in the Subject. + * + * @param id the ID to save + **/ + protected final void saveID(BigDecimal id) { + m_subject.getPrincipals().add(new PartyPrincipal(id)); + } + + /** + * Determines whether the current request is secure. + * + * @return <code>true</code> if the current request is secure, + * <code>false</code> otherwise. + * + * @throws LoginException if an error occurs. + **/ + protected final boolean isSecure() + throws LoginException { + if (m_secure == null) { + m_secure = new Boolean + (Util.getSecurityHelper().isSecure(getRequest())); + } + return m_secure.booleanValue(); + } + + /** + * Returns the name of the credential. + * @return the name of the credential. + * @throws LoginException if an error occurs. + **/ + protected abstract String getCredentialName() + throws LoginException; + + /** + * Returns the lifetime of the credential in milliseconds. + * @return the lifetime of the credential in milliseconds. + * @throws LoginException if an error occurs. + **/ + protected abstract long getLifetime() + throws LoginException; + + /** + * Loads the credential. + **/ + private void loadCredential() throws LoginException { + s_log.debug("START loadCredential"); + String value = m_manager.getValue(); + m_credential = Credential.parse(value); + s_log.debug("SUCCESS loadCredential: expires: " + +m_credential.getExpiration()); + } + + /** + * Determines whether the client session should be invalidated. + * + * @param value the new value for the credential + * + * @return <code>true</code> if the client session should be + * invalidated, <code>false</code> otherwise. + * + * @throws LoginException if an error occurs. + **/ + protected abstract boolean shouldInvalidateSession(String value) + throws LoginException; + + /** + * Invalidates the client session and creates a new one. + **/ + private void invalidateSession() + throws LoginException { + s_log.debug("invalidateSession: before: " + +getRequest().getSession().getId()); + + getRequest().getSession().invalidate(); + getRequest().getSession(); // create new session + + s_log.debug("invalidateSession: after: " + +getRequest().getSession().getId()); + } + + /** + * Sets the credential value to the given value. + **/ + private void setCredential(String value) + throws LoginException { + s_log.debug("START setCredential to "+value); + m_credential = Credential.create(value, getLifetime()); + m_manager.setValue(m_credential.toString()); + s_log.debug("SUCCESS setCredential: expires: " + +m_credential.getExpiration()); + } + + /** + * Determines whether the credential is set. Subclasses may call this + * to determine whether this login module succeeded. + * + * @return <code>true</code> if credential is set, <code>false</code> + * otherwise. + **/ + protected final boolean credentialIsSet() { + return (m_credential != null); + } + + /** + * Determines whether the credential has the given value. + * + * @param value the value to check + * + * @return <code>true</code> if credential's value equals the given + * value, <code>false</code> otherwise. + * + * @throws NullPointerException if !credentialIsSet(). + **/ + protected final boolean credentialHasValue(String value) { + return m_credential.getValue().equals(value); + } + + /** + * Determines whether the credential should be renewed. Returns + * <code>true</code> if the credential is more than + * <code>RENEW_SECS</code> old. + * + * @return <code>true</code> if credential is old; <code>false</code> + * otherwise. + * + * @throws NullPointerException if !credentialIsSet(). + **/ + protected final boolean credentialIsOld() { + long expireTime = m_credential.getExpiration().getTime() / 1000; + long issueTime = expireTime - TIMEOUT_SECS; + long renewTime = issueTime + RENEW_SECS; + long currentTime = System.currentTimeMillis() / 1000; + return renewTime < currentTime; + } + + /** + * Determines whether the requested URI ends in an "excluded" extension. + * Extensions in the "excluded" list specify file types for which + * credentials should never be set, such as image and media files. + * + * @return <code>true</code> if the request URI ends with an "excluded" + * extension, <code>false</code> otherwise. + * + * @throws LoginException if an error occurs. + **/ + protected final boolean requestIsExcluded() + throws LoginException { + java.util.Iterator exts = Initializer.getExcludedExtensions(); + while (exts.hasNext()) { + String ext = (String)exts.next(); + if (getRequest().getRequestURI().endsWith(ext)) { + s_log.debug("got excluded extension: " + +getRequest().getRequestURI()); + return true; + } + } + return false; + } + + /** + * Deletes the credential. + **/ + private void deleteCredential() + throws LoginException { + m_manager.deleteValue(); + m_credential = null; + } + + /** + * Returns the current HTTP request. + * + * @return the current HTTP request. + * + * @throws LoginException if an error occurs. + **/ + protected final HttpServletRequest getRequest() + throws LoginException + { + if (m_req == null) { + m_req = Util.getRequest(m_handler); + } + return m_req; + } + + /** + * Returns the current HTTP response. + * + * @return the current HTTP response. + * + * @throws LoginException if an error occurs. + **/ + protected final HttpServletResponse getResponse() + throws LoginException { + try { + if (m_res == null) { + HTTPResponseCallback cb = new HTTPResponseCallback(); + m_handler.handle(new Callback[] { cb }); + m_res = cb.getResponse(); + } + return m_res; + } catch (IOException e) { + throw new KernelLoginException + ("Could not get HTTP response", e); + } catch (UnsupportedCallbackException e) { + throw new KernelLoginException + ("Could not get HTTP response", e); + } + } + + /** + * Determines whether the credential should last "forever" or should + * expire at the end of this session. + * + * @return <code>true</code> if the credential should last "forever", + * <code>false</code> if the credential should expire at the end of this + * session. + * + * @throws KernelLoginException if an error occurs. + **/ + protected final boolean getForever() throws LoginException { + try { + if (m_forever == null) { + LifetimeCallback cb = new LifetimeCallback(); + m_handler.handle(new Callback[] { cb }); + m_forever = new Boolean(cb.isForever()); + } + return m_forever.booleanValue(); + } catch (IOException e) { + throw new KernelLoginException("Could not get lifetime", e); + } catch (UnsupportedCallbackException e) { + throw new KernelLoginException("Could not get lifetime", e); + } + } +} Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/UserLoginModule.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/UserLoginModule.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/com/arsdigita/kernel/security/UserLoginModule.java 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,114 @@ +/* + * 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.kernel.security; + +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.Kernel; + +/** + * Authenticates a user (loads a user ID) from a credential stored in the + * current HTTP request. + * + * @author Sameer Ajmani + **/ +public abstract class UserLoginModule extends CredentialLoginModule { + + public static final String versionId = "$Id: UserLoginModule.java 1477 2007-03-14 10:27:16Z chrisgilbert23 $ by $Author: chrisgilbert23 $, $DateTime: 2004/08/16 18:10:38 $"; + private static final Logger s_log = + Logger.getLogger(UserLoginModule.class.getName()); + + /** + * Name of the non-secure user authentication credential. + **/ + public static final String NORMAL_CREDENTIAL_NAME + = "ad_user_login"; + + /** + * Name of the secure user authentication credential. + **/ + public static final String SECURE_CREDENTIAL_NAME + = "ad_user_login"; + + // fields set by initialize() + private Subject m_subject; + private CallbackHandler m_handler; + private Map m_shared; + private Map m_options; + + public UserLoginModule(CredentialManager manager) { + super(manager); + } + + // implements LoginModule + public void initialize(Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + super.initialize(subject, handler, shared, options); + m_subject = subject; + m_handler = handler; + m_shared = shared; + m_options = options; + } + + /** + * Returns the name of the credential. + * + * @return <code>SECURE_CREDENTIAL_NAME</code> if the current request is + * secure, otherwise returns <code>NORMAL_CREDENTIAL_NAME</code>. + **/ + protected String getCredentialName() + throws LoginException { + if (isSecure()) { + return SECURE_CREDENTIAL_NAME; + } else { + return NORMAL_CREDENTIAL_NAME; + } + } + + /** + * Returns the lifetime of the credential in milliseconds. + * + * @return <code>FOREVER_SECS</code> in milliseconds if the user + * requests permanent login, otherwise returns <code>TIMEOUT_SECS</code> + * in milliseconds. + **/ + protected long getLifetime() throws LoginException { + Integer setting = Kernel.getSecurityConfig().getCookieDurationMinutes(); + long longSetting = setting == null ? TIMEOUT_SECS : (long)setting.intValue() * 60; + return 1000 * (getForever() ? FOREVER_SECS : longSetting); + } + + /** + * Determines whether the user's session should be invalidated. + * + * @param value the new value for the credential + * + * @return true if the credential is set and has the wrong value. + **/ + protected boolean shouldInvalidateSession(String value) + throws LoginException { + return credentialIsSet() && !credentialHasValue(value); + } +} Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepagePortalSelectionModel.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepagePortalSelectionModel.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepagePortalSelectionModel.java 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2001 ArsDigita Corporation. All Rights Reserved. + * + * The contents of this file are subject to the ArsDigita Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.arsdigita.com/ADPL.txt + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ +/** + * replacement for default version in com.arsdigita.aplaws.ui + * In actual fact this is identical, but it is required so that the + * constructor can take our custom version of HomepageWorkspaceSelectionModel + */ +package uk.gov.westsussex.aplaws.ui; + +import com.arsdigita.bebop.AbstractSingleSelectionModel; +import com.arsdigita.london.portal.ui.PortalSelectionModel; +import com.arsdigita.london.portal.WorkspacePage; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.parameters.ParameterModel; +import com.arsdigita.domain.DomainObject; + +public class HomepagePortalSelectionModel extends AbstractSingleSelectionModel + implements PortalSelectionModel { + + private HomepageWorkspaceSelectionModel m_workspace; + private int m_column; + + public HomepagePortalSelectionModel(HomepageWorkspaceSelectionModel workspace, + int column) { + m_workspace = workspace; + m_column = column; + } + + public void onCustomize(PageState state) { + m_workspace.onCustomize(state, m_column); + } + + public void onReset(PageState state) { + m_workspace.onReset(state, m_column); + } + + public HomepageWorkspaceSelectionModel getWorkspaceModel() { + return m_workspace; + } + + public Object getSelectedKey(PageState state) { + return getSelectedPortal(state).getID(); + } + + public void setSelectedKey(PageState state, + Object key) { + throw new UnsupportedOperationException("cannot set key"); + } + + public void setSelectedObject(PageState state, + DomainObject key) { + throw new UnsupportedOperationException("cannot set object"); + } + + public ParameterModel getStateParameter() { + throw new UnsupportedOperationException("not state param"); + } + + + public DomainObject getSelectedObject(PageState state) { + return getSelectedPortal(state); + } + + public WorkspacePage getSelectedPortal(PageState state) { + return m_workspace.getPortal(state, m_column); + } +} Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspace.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspace.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspace.java 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2001 ArsDigita Corporation. All Rights Reserved. + * + * The contents of this file are subject to the ArsDigita Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.arsdigita.com/ADPL.txt + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.aplaws.ui; + +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.london.portal.ui.PersistentPortal; +import com.arsdigita.london.portal.StatefulPersistentPortal; +import com.arsdigita.london.portal.Workspace; +import com.arsdigita.bebop.PageState; +import com.arsdigita.xml.Element; +import com.arsdigita.london.portal.ui.PortalConstants; +import com.arsdigita.kernel.permissions.PermissionService; +import com.arsdigita.kernel.permissions.PermissionDescriptor; +import com.arsdigita.kernel.permissions.PrivilegeDescriptor; +import com.arsdigita.kernel.Party; +import com.arsdigita.kernel.Kernel; + + +public class HomepageWorkspace extends SimpleContainer { + + private HomepagePortalSelectionModel m_model; + private ActionLink m_reset; + private ActionLink m_browse; + private ActionLink m_edit; + private StatefulPersistentPortal m_browser; + private PersistentPortal m_editor; + private boolean m_customizable; + private boolean m_readOnly; + private String m_name; + + public HomepageWorkspace() { + super("portal:homepageWorkspace", PortalConstants.PORTAL_XML_NS); + m_customizable = false; + } + + public void setModel(HomepagePortalSelectionModel model) { + m_model = model; + } + + public void setCustomizable(boolean customizable) { + m_customizable = customizable; + } + + public void setReadOnly(boolean readOnly) { + m_readOnly = readOnly; + } + + public void setName(String name) { + m_name = name; + } + + public void addWidgets() { + m_edit = new ActionLink("customize"); + m_browse = new ActionLink("browse"); + m_reset = new ActionLink("reset"); + m_reset.setConfirmation("Are you sure you wish to reset this column? " + + "This will permanently remove all portlets."); + + m_browser = new StatefulPersistentPortal(m_model, + m_name); + m_editor = new PersistentPortal(m_model, + m_name, + PortalConstants.MODE_EDITOR); + + m_edit.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PageState state = e.getPageState(); + setDisplayMode(state, false); + + m_model.onCustomize(state); + } + }); + m_browse.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PageState state = e.getPageState(); + setDisplayMode(state, true); + } + }); + m_reset.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PageState state = e.getPageState(); + setDisplayMode(state, true); + + m_model.onReset(state); + } + }); + + add(m_edit); + add(m_browse); + add(m_reset); + add(m_editor); + add(m_browser); + } + + public void setDisplayMode(PageState state, + boolean browse) { + if (m_readOnly) { + return; + } + m_browse.setVisible(state, !browse); + m_reset.setVisible(state, !browse); + m_edit.setVisible(state, browse); + m_browser.setVisible(state, browse); + m_editor.setVisible(state, !browse); + } + + public void register(Page page) { + super.register(page); + + page.setVisibleDefault(m_browse, false); + page.setVisibleDefault(m_reset, false); + page.setVisibleDefault(m_edit, !m_readOnly); + page.setVisibleDefault(m_browser, true); + page.setVisibleDefault(m_editor, false); + } + + public void generateXML(PageState state, + Element parent) { + Party party = Kernel.getContext().getParty(); + Workspace global = m_model.getWorkspaceModel().getGlobalWorkspace(state); + PermissionDescriptor admin = + new PermissionDescriptor(PrivilegeDescriptor.ADMIN, + global, + party); + boolean hasAdmin = PermissionService.checkPermission(admin); + + if (party == null || m_readOnly || + (!hasAdmin && !m_customizable)) { + m_reset.setVisible(state, false); + m_browse.setVisible(state, false); + m_edit.setVisible(state, false); + } + + super.generateXML(state, parent); + } + +} Added: aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspaceSelectionModel.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspaceSelectionModel.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wsgfl-custom/src/uk/gov/westsussex/aplaws/ui/HomepageWorkspaceSelectionModel.java 2007-09-17 10:00:38 UTC (rev 1635) @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2001 ArsDigita Corporation. All Rights Reserved. + * + * The contents of this file are subject to the ArsDigita Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.arsdigita.com/ADPL.txt + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ +/** + * Replacement for default version in com.arsdigita.aplaws.ui - simply adds an extra workspace page, + * as we didn't want the whole of the right hand side to disappear as soon as a user customizes it + +//to do - sort this out so it is flexible enough to deal with any number of pages - +//generally straightforward - only problem arises in getPortal - there is currently no +//persistent information to tell us that portal is customizable or not +//could be a bit tricky + +*/ + +package uk.gov.westsussex.aplaws.ui; + +import org.apache.log4j.Logger; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.kernel.Kernel; +import com.arsdigita.kernel.Party; +import com.arsdigita.kernel.User; +import com.arsdigita.kernel.permissions.PermissionDescriptor; +import com.arsdigita.kernel.permissions.PermissionService; +import com.arsdigita.kernel.permissions.PrivilegeDescriptor; +import com.arsdigita.london.portal.Workspace; +import com.arsdigita.london.portal.WorkspacePage; +import com.arsdigita.london.portal.WorkspacePageCollection; +import com.arsdigita.london.subsite.Subsite; +import com.arsdigita.util.Assert; +import com.arsdigita.util.UncheckedWrapperException; + +public class HomepageWorkspaceSelectionModel { + private RequestLocal m_loaded = new RequestLocal(); + private RequestLocal m_global = new RequestLocal(); + // custom Workspace + private RequestLocal m_personal = new RequestLocal(); + // workspacePages + private RequestLocal m_left = new RequestLocal(); + private RequestLocal m_middle = new RequestLocal(); + private RequestLocal m_bottomMiddle = new RequestLocal(); + private RequestLocal m_bottomMiddleCustom = new RequestLocal(); + private RequestLocal m_right = new RequestLocal(); + private RequestLocal m_bottomRight = new RequestLocal(); + private RequestLocal m_bottomRightCustom = new RequestLocal(); + + private static Logger s_log = + Logger.getLogger(HomepageWorkspaceSelectionModel.class.getName()); + + public WorkspacePage getPortal(PageState state, int column) { + if (!Boolean.TRUE.equals(m_loaded.get(state))) { + loadWorkspacePages(state); + } + + if (column == 0) { // Always global portal + return (WorkspacePage) m_left.get(state); + } else if (column == 1) { // Always global portal + return (WorkspacePage) m_middle.get(state); + } else if (column == 2) { // Always global portal + return (WorkspacePage) m_right.get(state); + } else if ( + column == 3) { // Right Hand Personal portal, fallback on global + Party party = (Party) Kernel.getContext().getParty(); + WorkspacePage ri... [truncated message content] |
Author: chrisg23 Date: 2007-09-17 12:00:20 +0200 (Mon, 17 Sep 2007) New Revision: 1634 Added: aplaws/contrib/wsx/ccm-wsx-atomwide/application.xml aplaws/contrib/wsx/ccm-wsx-atomwide/lib/ aplaws/contrib/wsx/ccm-wsx-atomwide/lib/commons-lang-2.1.jar aplaws/contrib/wsx/ccm-wsx-atomwide/lib/msbase.jar aplaws/contrib/wsx/ccm-wsx-atomwide/lib/mssqlserver.jar aplaws/contrib/wsx/ccm-wsx-atomwide/lib/msutil.jar aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/ aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/ aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/ aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/ aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/Atomwide.pdl aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/SchoolUser.pdl aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/oracle-se-create.sql aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/postgres-create.sql aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/default/ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/default/1.0.2-1.0.3/ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/default/1.0.2-1.0.3/code_mapping_table.sql aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/oracle-se-1.0.2-1.0.3.sql aplaws/contrib/wsx/ccm-wsx-atomwide/src/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.config aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.load aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.upgrade aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/Atomwide.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideAuthenticationModule.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig_parameter.properties aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConstants.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideMappingLoginModule.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolInformationProvider.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolListener.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideUserIdentifier.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/CreateTopFolders.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/FolderCreator.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/Initializer.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/Loader.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/RoleGrouping.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/School.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/SchoolListener.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/SchoolUser.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/ActionPanel.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/AddRoleGrouping.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/AtomwideAdminConstants.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/AtomwideAdminResources_en.properties aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/CategoryPanel.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/CategorySelectionModel.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/CategoryTree.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/CreateFolderForm.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/CreateGroupsForm.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/CurrentPromotionsPanel.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/EditRoleGrouping.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/LinkForm.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/LinkOrPromotionsPanel.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/MainAdminPage.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/RoleGroupingAdministrationPanel.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/RoleGroupingContainer.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/RoleGroupingInfo.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/admin/ui/RoleGroupingListing.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/AtomwideProfile.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/AtomwideProfileDAO.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/AtomwideSchoolDAO.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/AtomwideSchoolData.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/AtomwideUser.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/AtomwideUserDAO.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/data/AtomwideUserLite.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/BinaryMapper.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/BinaryMapperFactory.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/BitPairBinaryMapper.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/CodeMapping.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/RoleMapper.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/Roles.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/SchoolMapper.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/mapping/SingleBitBinaryMapper.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/upgrade/ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/upgrade/NewSchoolClassification.java aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/upgrade/RemoveSchoolClassification.java aplaws/contrib/wsx/ccm-wsx-atomwide/web/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/META-INF/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/META-INF/MANIFEST.MF aplaws/contrib/wsx/ccm-wsx-atomwide/web/WEB-INF/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/WEB-INF/ibm-web-bnd.xmi aplaws/contrib/wsx/ccm-wsx-atomwide/web/WEB-INF/ibm-web-ext.xmi aplaws/contrib/wsx/ccm-wsx-atomwide/web/WEB-INF/web.xml aplaws/contrib/wsx/ccm-wsx-atomwide/web/__ccm__/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/__ccm__/apps/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/__ccm__/apps/atomwide/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/__ccm__/apps/atomwide/xsl/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/__ccm__/apps/atomwide/xsl/index.xsl aplaws/contrib/wsx/ccm-wsx-atomwide/web/templates/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/templates/ccm-wsx-atomwide/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/templates/ccm-wsx-atomwide/admin/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/templates/ccm-wsx-atomwide/admin/atomwide/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/templates/ccm-wsx-atomwide/admin/atomwide/index.jsp aplaws/contrib/wsx/ccm-wsx-atomwide/web/theme/ aplaws/contrib/wsx/ccm-wsx-atomwide/web/theme/Master.css Log: Module to allow authentication from an Atomwide School User Directory. Module also manages domains for Schools recorded in Atomwide and user profile values to allow personalisation and access control for school users based on their Atomwide Profile. Added: aplaws/contrib/wsx/ccm-wsx-atomwide/application.xml =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/application.xml (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/application.xml 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<ccm:application name="ccm-wsx-atomwide" + prettyName="Atomwide School User Authentication Modules" + version="1.0.3" + release="1" + webapp="ROOT" + xmlns:ccm="http://ccm.redhat.com/ccm-project"> + + <ccm:dependencies> + <ccm:requires name="ccm-wsx-news-portlet" version="1.0.1"/> + <ccm:requires name="ccm-wsx-personalised-links-portlet" version="1.0.1"/> + <ccm:requires name="ccm-wsx-authentication" version="1.0.1"/> + <ccm:requires name="ccm-wsx-wsgfl-custom" version="1.0.2"/> + </ccm:dependencies> + + <ccm:directories> + <ccm:directory name="pdl"/> + <ccm:directory name="web"/> + <ccm:directory name="src"/> + <ccm:directory name="sql"/> + </ccm:directories> + + <ccm:contacts> + <ccm:contact uri="http://wsgfl.westsussex.gov.uk" type="website"/> + <ccm:contact uri="mailto:chr...@we..." type="support"/> + </ccm:contacts> + + <ccm:description> + Authentication modules, and mechanisms for synchronising group and profile information + </ccm:description> +</ccm:application> Added: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/commons-lang-2.1.jar =================================================================== (Binary files differ) Property changes on: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/commons-lang-2.1.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/msbase.jar =================================================================== (Binary files differ) Property changes on: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/msbase.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/mssqlserver.jar =================================================================== (Binary files differ) Property changes on: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/mssqlserver.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/msutil.jar =================================================================== (Binary files differ) Property changes on: aplaws/contrib/wsx/ccm-wsx-atomwide/lib/msutil.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/Atomwide.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/Atomwide.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/Atomwide.pdl 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,113 @@ +model uk.gov.westsussex.atomwide; + +import com.arsdigita.web.Application; +import com.arsdigita.london.terms.Term; +import com.arsdigita.kernel.Group; + +object type Atomwide extends Application { + +} + + +object type CodeMapper { + + Integer[1..1] mappingID = atomwide_ced_mapping.mapping_id INTEGER; + String[1..1] codeType = atomwide_ced_mapping.code_type VARCHAR(255); + String[0..1] codeValue = atomwide_ced_mapping.code_value VARCHAR(255); + Term[0..1] parentTerm = join atomwide_ced_mapping.parent_term to trm_terms.term_id; + Term[0..1] navigationTerm = join atomwide_ced_mapping.navigation_term to trm_terms.term_id; + unique (codeType, codeValue); + + object key (mappingID); + + +} +// subquery needed because we can't add two filters on a group collection so +// that group is categorised under a role grouping, and under a school category +// as the generated query tries to fulfil both conditions on the +// same category + +query GroupForCategories { + Group group; + do { + + select a.group_id, + e.object_type, + e.display_name, + e.default_domain_class, + d.primary_email, + d.uri, + a.name + from groups a, + parties d, + acs_objects e, + cat_object_category_map b, + cat_object_category_map c + where a.group_id = b.object_id + and a.group_id = c.object_id + and b.category_id = :roleGrouping + and c.category_id = :school + and d.party_id = a.group_id + and e.object_id = a.group_id + + } map { + group.id = a.group_id; + group.name = a.name; + group.primaryEmail = d.primary_email; + group.uri = d.uri; + group.displayName = e.display_name; + group.defaultDomainClass = e.default_domain_class; + group.objectType = e.object_type; + + } +} + + +// use for in subqueries eg +// select * from groups g +// where g.group_id in (select object_id from CAT_OBJECT_CATEGORY_MAP where category_id = 21385) +// and g.group_id in (select object_id from CAT_OBJECT_CATEGORY_MAP where category_id = 22701) +// to find the group categorised under a particular school AND a particular role grouping +query ObjectsForCategory { + BigDecimal objectID; + do { + select object_id + from CAT_OBJECT_CATEGORY_MAP + where category_id = :catID + } map { + objectID = object_id; + } +} + + +// oh dear - if I try to do the above, the persistance layer cannot distinguish between +// the 2 different bind variables called catID, so I need to have 2 queries +query ObjectsForCategory2 { + BigDecimal objectID; + do { + select object_id + from CAT_OBJECT_CATEGORY_MAP + where category_id = :catID2 + } map { + objectID = object_id; + } +} + +query ItemsInGroupingFolders { + Integer flag; + do { + select 1 as flag + from cms_items + where parent_id in + (select folder_id + from cms_folders a, + cat_object_category_map b + where a.folder_id = b.object_id + and b.category_id = :groupingID) + } map { + flag = flag; + } +} + + + Added: aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/SchoolUser.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/SchoolUser.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/pdl/uk/gov/westsussex/atomwide/SchoolUser.pdl 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,77 @@ +model uk.gov.westsussex.atomwide; + +import com.arsdigita.kernel.Group; +import com.arsdigita.cms.ContentPage; + +query getUserProfileGroups { + Group group; + do { + +select a.group_id , + b.object_type, + b.display_name, + b.default_domain_class, + c.primary_email, + c.uri, + a.name + from groups a +join acs_objects b on a.group_id = b.object_id +join parties c on a.group_id = c.party_id +left join cat_object_category_map d on a.group_id = d.object_id +left join cat_object_category_map f on a.group_id = f.object_id +where d.category_id in :userRoles and f.category_id in :userSchools + + } map { + group.id = a.group_id; + group.name = a.name; + group.primaryEmail = c.primary_email; + group.uri = c.uri; + group.displayName = b.display_name; + group.defaultDomainClass = b.default_domain_class; + group.objectType = b.object_type; + } +} + + +// in query below, both joins on cat_categories looked like this +// left join cat_categories g on g.category_id = f.category_id and f.category_id is not null +// removing the not null halved the time for the query + +query getPersonalisedNews { + ContentPage page; + options { + WRAP_QUERIES = false; + } + do { + +select distinct page.item_id, + page.title, + page.launch_date, + item.version, + item.name, + item.language, + item.ancestors, + acs.object_type, + acs.display_name, + acs.default_domain_class +from cms_pages page +join cms_items item on page.item_id = item.item_id +join acs_objects acs on page.item_id = acs.object_id +left join cat_object_category_map d on item.parent_id = d.object_id +left join cat_object_category_map f on item.parent_id = f.object_id +where d.category_id in :userRoles and f.category_id in :userSchools +and item.version = :version +} map { + page.id = page.item_id; + page.title = page.title; + page.launchDate = page.launch_date; + page.version = item.version; + page.name = item.name; + page.language = item.language; + page.ancestors = item.ancestors; + page.displayName = acs.display_name; + page.defaultDomainClass = acs.default_domain_class; + page.objectType = acs.object_type; + } +} + \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/oracle-se-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/oracle-se-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/oracle-se-create.sql 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1 @@ +@ ddl/oracle-se/create.sql Added: aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/postgres-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/postgres-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/postgres-create.sql 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,3 @@ +begin; +\i ddl/postgres/create.sql +end; Added: aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/default/1.0.2-1.0.3/code_mapping_table.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/default/1.0.2-1.0.3/code_mapping_table.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/default/1.0.2-1.0.3/code_mapping_table.sql 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,21 @@ + +create table atomwide_ced_mapping ( + mapping_id INTEGER not null + constraint atomw_ced_mapp_mapp_id_p_uz1s0 + primary key, + code_type VARCHAR(255) not null, + code_value VARCHAR(255), + parent_term INTEGER, + -- referential constraint for parent_term deferred due to circular dependencies + navigation_term INTEGER, + -- referential constraint for navigation_term deferred due to circular dependencies + constraint atom_ced_map_cod_typ_c_u_zikpc + unique(code_type, code_value) +); + +alter table atomwide_ced_mapping add + constraint atom_ced_map_navi_term_f_quzdr foreign key (navigation_term) + references trm_terms(term_id); +alter table atomwide_ced_mapping add + constraint atom_ced_mapp_par_term_f_bwah6 foreign key (parent_term) + references trm_terms(term_id); Added: aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/oracle-se-1.0.2-1.0.3.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/oracle-se-1.0.2-1.0.3.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/sql/ccm-wsx-atomwide/upgrade/oracle-se-1.0.2-1.0.3.sql 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1 @@ +@@ default/1.0.2-1.0.3/code_mapping_table.sql Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.config =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.config (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.config 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<registry> + <config class="uk.gov.westsussex.atomwide.AtomwideConfig" + storage="ccm-wsx-atomwide/atomwide.properties"/> +</registry> Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.load =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.load (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.load 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,18 @@ +<load> + <requires> + <table name="inits"/> + <table name="acs_objects"/> + <initializer class="com.arsdigita.core.Initializer"/> + <initializer class="com.arsdigita.cms.Initializer"/> + <initializer class="com.arsdigita.london.terms.Initializer"/> + + </requires> + <provides> + <initializer class="uk.gov.westsussex.atomwide.Initializer"/> + </provides> + <scripts> + <data class="uk.gov.westsussex.atomwide.Loader"/> + </scripts> +</load> + + Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.upgrade =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.upgrade (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/ccm-wsx-atomwide.upgrade 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,5 @@ +<upgrade> + <version from="1.0.2" to="1.0.3"> + <script sql="ccm-wsx-atomwide/upgrade/::database::-1.0.2-1.0.3.sql"/> + </version> +</upgrade> Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/Atomwide.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/Atomwide.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/Atomwide.java 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.atomwide; + +import java.net.MalformedURLException; +import java.net.URL; +import java.sql.SQLException; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import uk.gov.westsussex.atomwide.mapping.RoleMapper; +import uk.gov.westsussex.atomwide.mapping.SchoolMapper; + +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.london.terms.Domain; +import com.arsdigita.london.terms.Term; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.OID; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.web.Application; + +/** + * Main application class, with some static methods + * + * @author cgyg9330 + * + */ +public class Atomwide extends Application implements AtomwideConstants { + + /** + * listeners interested in school related events - new school, removed + * school, renamed school. Still used by emergency contacts and school visits, but + * but should be deprecated in favour of the EstablishmentListener in + * ccm-wsx-wsgfl-custom. + * + * school related events occur during server restart, when terms in the + * schools domain are synchronised with the schools in Atomwide + * + */ + private static Set schoolListeners = new HashSet(); + + private static AtomwideConfig s_config = new AtomwideConfig(); + + static { + s_config.load(); + } + + public static void registerNewSchoolListener(SchoolListener listener) { + schoolListeners.add(listener); + + } + + public static Set getSchoolListeners() { + return schoolListeners; + } + + public static AtomwideConfig getConfig() { + return s_config; + } + + public static final String BASE_DATA_OBJECT_TYPE = "uk.gov.westsussex.atomwide.Atomwide"; + + public Atomwide(DataObject obj) { + super(obj); + } + + public Atomwide(OID oid) { + super(oid); + } + + public String getServletPath() { + return "/ccm-wsx-atomwide/files"; + } + + public static void synchronise() { + createDomains(); + createTerms(); + + try { + RoleMapper.retrieve().updateAllRoles(); + } catch (SQLException e) { + throw new UncheckedWrapperException(e); + } + try { + SchoolMapper.retrieve().loadSchools(); + } catch (SQLException e) { + throw new UncheckedWrapperException(e); + } + + } + + /** + * Create necessary domains if they don't already exist. As this is + * persistent, this method only ever happens once per database instance. + * + */ + private static void createDomains() { + try { + Domain.retrieve(SCHOOLS_DOMAIN_KEY); + // it's there, so the other one is too - jump out early + return; + } catch (DataObjectNotFoundException e) { + URL url = null; + try { + url = new URL(ATOMWIDE_PERSONALISATION_DOMAIN_URL + + "schools/1.01"); + } catch (MalformedURLException me) { + throw new UncheckedWrapperException(me); + } + Domain + .create( + SCHOOLS_DOMAIN_KEY, + url, + "School list", + "Hierarchy of schools and other establishments in West Sussex. Do not amend the terms in this domain. They are derived automatically.", + "1.01", new Date()); + + // add mapping to content application - not sure how to do it in code + // Do it manually (in terms admin) - you can't assign personalisation + // categories when authoring until that's done + } + try { + Domain.retrieve(ROLES_DOMAIN_KEY); + } catch (DataObjectNotFoundException e) { + URL url = null; + try { + url = new URL(ATOMWIDE_PERSONALISATION_DOMAIN_URL + + "roles/1.01"); + } catch (MalformedURLException me) { + throw new UncheckedWrapperException(me); + } + Domain + .create( + ROLES_DOMAIN_KEY, + url, + "School User Role list", + "Hierarchy of roles that apply to school users. Do not amend the terms in this domain. They are derived automatically. Role grouping terms are administered under CCM Admin.", + "1.01", new Date()); + + } + + } + + /** + * Create necessary terms if they don't already exist. As this is + * persistent, this method only ever happens once per database instance. + * + */ + private static void createTerms() { + Domain rolesDomain = Domain.retrieve(ROLES_DOMAIN_KEY); + Term allRolesTerm = null; + Term term = null; + try { + allRolesTerm = rolesDomain.getTerm(ALL_ROLES_ID); + // it's there, so the others are too - jump out early + return; + } catch (DataObjectNotFoundException e) { + allRolesTerm = Term.create(ALL_ROLES_ID, "All Roles", false, null, + rolesDomain); + rolesDomain.addRootTerm(allRolesTerm); + } + + try { + term = rolesDomain.getTerm(ROLE_GROUPINGS_ID); + } catch (DataObjectNotFoundException e) { + term = Term.create(ROLE_GROUPINGS_ID, "Role Groupings", false, + null, rolesDomain); + rolesDomain.addRootTerm(term); + } + try { + term = rolesDomain.getTerm(TEACHER_ROLE_ID); + } catch (DataObjectNotFoundException e) { + term = Term.create(TEACHER_ROLE_ID, "Teacher", false, null, + rolesDomain); + allRolesTerm.addNarrowerTerm(term, true, true); + } + + try { + term = rolesDomain.getTerm(PUPIL_ROLE_ID); + } catch (DataObjectNotFoundException e) { + term = Term + .create(PUPIL_ROLE_ID, "Pupil", false, null, rolesDomain); + allRolesTerm.addNarrowerTerm(term, true, true); + } + + Domain schoolsDomain = Domain.retrieve(SCHOOLS_DOMAIN_KEY); + Term allSchoolsTerm = null; + + try { + allSchoolsTerm = schoolsDomain.getTerm(ALL_SCHOOLS_ID); + } catch (DataObjectNotFoundException e) { + allSchoolsTerm = Term.create(ALL_SCHOOLS_ID, "All Schools", false, + null, schoolsDomain); + schoolsDomain.addRootTerm(allSchoolsTerm); + } + + try { + term = schoolsDomain.getTerm(NURSERY_SCHOOLS); + } catch (DataObjectNotFoundException e) { + term = Term.create(NURSERY_SCHOOLS, "All Nursery Schools", false, + null, schoolsDomain); + allSchoolsTerm.addNarrowerTerm(term, true, true); + } + try { + term = schoolsDomain.getTerm(PRIMARY_SCHOOLS); + } catch (DataObjectNotFoundException e) { + term = Term.create(PRIMARY_SCHOOLS, "All Primary Schools", false, + null, schoolsDomain); + allSchoolsTerm.addNarrowerTerm(term, true, true); + } + try { + term = schoolsDomain.getTerm(SECONDARY_SCHOOLS); + } catch (DataObjectNotFoundException e) { + term = Term.create(SECONDARY_SCHOOLS, "All Secondary Schools", + false, null, schoolsDomain); + allSchoolsTerm.addNarrowerTerm(term, true, true); + } + try { + term = schoolsDomain.getTerm(SPECIAL_SCHOOLS); + } catch (DataObjectNotFoundException e) { + term = Term.create(SPECIAL_SCHOOLS, "All Special Schools", false, + null, schoolsDomain); + allSchoolsTerm.addNarrowerTerm(term, true, true); + } + try { + term = schoolsDomain.getTerm(OTHER_ESTABLISHMENTS); + } catch (DataObjectNotFoundException e) { + term = Term.create(OTHER_ESTABLISHMENTS, + "All Other Establishments", false, null, schoolsDomain); + allSchoolsTerm.addNarrowerTerm(term, true, true); + } + } + +} Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideAuthenticationModule.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideAuthenticationModule.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideAuthenticationModule.java 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,83 @@ + +package uk.gov.westsussex.atomwide; + +import java.sql.SQLException; +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.atomwide.data.AtomwideUser; + +import com.arsdigita.kernel.security.AccountNotFoundException; +import com.arsdigita.kernel.security.PasswordLoginModule; + +/** + * @author cgyg9330 + * + * Authenticates user against the Atomwide SQLServer directory. No action on success - JAAS just proceeds to the next loginmodule + */ +public class AtomwideAuthenticationModule extends PasswordLoginModule { + + + public static final String ATOMWIDE_USER = "atomwideUser"; + + private static final Logger s_log = + Logger.getLogger(AtomwideAuthenticationModule.class); + + private Map m_shared; + + // implements LoginModule + public void initialize( + Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + super.initialize(subject, handler, shared, options); + m_shared = shared; + + } + + /** + * @see com.arsdigita.kernel.security.PasswordLoginModule#checkPassword(String, char[]) + */ + protected void checkPassword(String userID, char[] password) + throws LoginException { + + try { + userID = userID.toLowerCase(); + AtomwideUser user = new AtomwideUser(userID); + + boolean success = user.authenticate(new String(password)); + if (!success) { + throw new FailedLoginException(); + } + s_log.debug("Authentication succeeded"); + // cache the user in shared space to save looking up again in the mapping module + m_shared.put(ATOMWIDE_USER, user); + + } catch (SQLException e) { + s_log.error("A database error occurred", e); + throw new LoginException("A database error occurred - please contact the ICTS Helpdesk"); + } catch (AccountNotFoundException e) { + throw new LoginException("User ID " + userID + " could not be found"); + } + } + + public boolean commit() throws LoginException { + return false; + } + + public boolean abort() throws LoginException { + return false; + } + + public boolean logout() throws LoginException { + return false; + } + +} Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig.java 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.atomwide; + +import com.arsdigita.runtime.AbstractConfig; +import com.arsdigita.util.parameter.Parameter; +import com.arsdigita.util.parameter.StringParameter; + +/** + * Specification of datasources required by Atomwide application + * + * @author Chris Gilbert <chr...@we...> + * @version $Id: AtomwideConfig.java,v 1.3 2007/07/31 13:15:37 cgyg9330 Exp $ + */ +public class AtomwideConfig extends AbstractConfig { + public final static String versionId = "$Id: AtomwideConfig.java,v 1.3 2007/07/31 13:15:37 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + "$DateTime: 2004/03/18 11:10:20 $"; + + + /** + * name for pooled datasource as defined in conf/resin.conf + */ + private StringParameter atomwideDatasourceNameParameter = new StringParameter( + "uk.gov.westsussex.atomwide.atomwide_datasource_jndi_name", + Parameter.REQUIRED, "jdbc/atomwide"); + + /** + * standalone connection url (used by command line programs that don't run + * within the Resin container + */ + private StringParameter atomwideJdbcURLParameter = new StringParameter( + "uk.gov.westsussex.atomwide.atomwide_jdbc_url", + Parameter.REQUIRED, + "jdbc:microsoft:sqlserver://172.21.66.254:1433;User=user.portal;Password=W-suss3x"); + + + /** + * needs to be looked up programatically by the folder creator. Currently this is + * specified as name (though should probably be done by term unique id) + */ + private StringParameter headTeacherRoleGrouping = new StringParameter( + "uk.gov.westsussex.atomwide.headteacher_role_grouping", + Parameter.REQUIRED, "Headteachers"); + + + + public AtomwideConfig() { + + register(atomwideDatasourceNameParameter); + register(atomwideJdbcURLParameter); + register(headTeacherRoleGrouping); + loadInfo(); + } + + public final String getAtomwideDatasourceName() { + return (String) get(atomwideDatasourceNameParameter); + } + + + + public final String getAtomwideJdbcURL() { + return (String) get(atomwideJdbcURLParameter); + } + + + + public final String getHeadTeacherRoleGrouping() { + return (String) get(headTeacherRoleGrouping); + } + + +} Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig_parameter.properties =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig_parameter.properties (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConfig_parameter.properties 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,18 @@ +uk.gov.westsussex.atomwide.atomwide_datasource_jndi_name.title=Atomwide Datasource JNDI Name +uk.gov.westsussex.atomwide.atomwide_datasource_jndi_name.purpose=JNDI Name within java:comp/env of datasource for Atomwide directory +uk.gov.westsussex.atomwide.atomwide_datasource_jndi_name.example=jdbc/atomwide +uk.gov.westsussex.atomwide.atomwide_datasource_jndi_name.format=[string] + + +uk.gov.westsussex.atomwide.atomwide_jdbc_url.title=Atomwide standalone connection jdbc url +uk.gov.westsussex.atomwide.atomwide_jdbc_url.purpose=jdbc connection url for use when datasource not available, must include username and password +uk.gov.westsussex.atomwide.atomwide_jdbc_url.example=jdbc:microsoft:sqlserver://172.21.66.254:1433;User=user;Password=password +uk.gov.westsussex.atomwide.atomwide_jdbc_url.format=[string] + + +uk.gov.westsussex.atomwide.headteacher_role_grouping.title=Headteacher role grouping name +uk.gov.westsussex.atomwide.headteacher_role_grouping.purpose=Headteacher role grouping is used by folder creator, so needs to be identifiable +uk.gov.westsussex.atomwide.headteacher_role_grouping.example=Headteachers +uk.gov.westsussex.atomwide.headteacher_role_grouping.format=[string] + + Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConstants.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConstants.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideConstants.java 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,54 @@ +/* + * Created on 08-Feb-05 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.atomwide; + + +/** + * @author cgyg9330 + * + * Unique IDs for domains and terms that are created during loading of application + */ +public interface AtomwideConstants { + + + public static final String CONNECTION_NAME = "atomwide"; + + public static final String SCHOOLS_DOMAIN_KEY = "schools"; + public static final String ROLES_DOMAIN_KEY = "roles"; + public static final String ATOMWIDE_PERSONALISATION_DOMAIN_URL = "http://wsgfl.westsussex.gov.uk/personalisation/atomwide/"; + + public static final Integer ALL_ROLES_ID = new Integer(1000); + public static final Integer TEACHER_ROLE_ID = new Integer(2000); + public static final Integer PUPIL_ROLE_ID = new Integer(3000); + public static final Integer ROLE_GROUPINGS_ID = new Integer(4000); + + // dfes numbers are 4 digit codes, so we need to use IDs outside this range + public static final Integer ALL_SCHOOLS_ID = new Integer (10000); + public static final Integer NURSERY_SCHOOLS = new Integer (10001); + public static final Integer PRIMARY_SCHOOLS = new Integer (10002); + public static final Integer SECONDARY_SCHOOLS = new Integer (10003); + public static final Integer SPECIAL_SCHOOLS = new Integer (10004); + public static final Integer OTHER_ESTABLISHMENTS = new Integer (10005); + + public static final String WEST_SUSSEX_LEA_ID = "938"; + + + public static Integer[] SPECIAL_ROLES = new Integer[]{ALL_ROLES_ID, ROLE_GROUPINGS_ID, TEACHER_ROLE_ID, PUPIL_ROLE_ID}; + //public static Integer[] SPECIAL_SCHOOL_TERMS = new Integer[]{ALL_SCHOOLS_ID, NURSERY_SCHOOLS, PRIMARY_SCHOOLS, SECONDARY_SCHOOLS, SPECIAL_SCHOOLS, OTHER_ESTABLISHMENTS}; + + public static final String ATOMWIDE_MAIL_DOMAIN = "wsgfl.org.uk"; + + + public static final Integer ALL_MEMBERS_ID = new Integer(4998); + public static final Integer ALL_STAFF_ID = new Integer(4999); + + public static final Integer UNALLOCATED_SCHOOL_ID = new Integer(9999); + + public static String ALL_MEMBERS = "All Members"; + public static String ALL_STAFF = "All Staff"; + +} Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideMappingLoginModule.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideMappingLoginModule.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideMappingLoginModule.java 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,138 @@ +package uk.gov.westsussex.atomwide; + +import java.math.BigDecimal; +import java.sql.SQLException; +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.atomwide.data.AtomwideUser; +import uk.gov.westsussex.authentication.UserTracker; +import uk.gov.westsussex.authentication.Utils; + +import com.arsdigita.domain.DomainCollection; +import com.arsdigita.kernel.EmailAddress; +import com.arsdigita.kernel.PersonName; +import com.arsdigita.kernel.User; +import com.arsdigita.kernel.security.AccountNotFoundException; +import com.arsdigita.kernel.security.MappingLoginModule; +import com.arsdigita.london.terms.Term; +import com.arsdigita.persistence.DataCollection; +import com.arsdigita.persistence.SessionManager; + +/** + * @author cgyg9330 + * + * Locates Aplaws user for the retrieved Atomwide user record, + * and associates Aplaws Terms (representing roles and schools) + * with the user, and based on any rolegroupings that include the + * assigned roles, gives the user appropriate group membership. + */ +public class AtomwideMappingLoginModule extends MappingLoginModule { + + private static final Logger s_log = + Logger.getLogger(AtomwideMappingLoginModule.class); + + private Map m_shared; + + // implements LoginModule + public void initialize( + Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + super.initialize(subject, handler, shared, options); + m_shared = shared; + + } + + /* (non-Javadoc) + * @see com.arsdigita.kernel.security.MappingLoginModule#getUserID(java.lang.String) + */ + protected BigDecimal getUserID(String atomwideUserID) + throws AccountNotFoundException, LoginException { + atomwideUserID = atomwideUserID.toLowerCase(); + s_log.debug("Start - getUserID for " + atomwideUserID); + User user = getUser(atomwideUserID); + s_log.debug("END - getUserID"); + return user.getID(); + + } + + private User getUser(String atomwideUserID) + throws AccountNotFoundException, LoginException { + AtomwideUser atomwideUser = + (AtomwideUser) m_shared.get( + AtomwideAuthenticationModule.ATOMWIDE_USER); + + if (null == atomwideUser) { + // could happen - not sure how but Matt Booth did think up a possible example + try { + // atomwideUser represents the SQLServer record + atomwideUser = new AtomwideUser(atomwideUserID); + + } catch (SQLException e) { + throw new LoginException( + "database error occurred when trying to retrieve user details from Atomwide database - " + + e.getMessage()); + } + } + + // locate Aplaws user object (using email address as lookup key) + User user; + DataCollection users = + SessionManager.getSession().retrieve(User.BASE_DATA_OBJECT_TYPE); + users.addEqualsFilter("primaryEmail", atomwideUser.getEmail()); + // email address is used as unique identifier + if (users.next()) { + s_log.debug("retrieving existing wsgfl user"); + user = User.retrieve(users.getDataObject()); + + } else { + s_log.debug("creating new wsgfl user"); + + user = new User(true); + UserTracker.setNew(user); + + + } + users.close(); + + // set or update details according to current retrieved record from Atomwide + user.setPrimaryEmail(new EmailAddress(atomwideUser.getEmail())); + PersonName name = user.getPersonName(); + // added 29/6/04 - new requirement for integration with Jive forums. User names must be unique + // Utils class deals with addition of digits to the end of surnames + // if no need for unique names, then just set givenname and familyname of personname + + Utils.setCCMName( + name, + atomwideUser.getFirstName(), + atomwideUser.getLastName()); + + + // schoolUser is wrapper around Aplaws user for managing term/group assignment + SchoolUser schoolUser = new SchoolUser(user); + schoolUser.initialise(atomwideUser); + DomainCollection schools = schoolUser.getSchools(); + schools.addEqualsFilter(Term.UNIQUE_ID, AtomwideConstants.UNALLOCATED_SCHOOL_ID); + if (schools.size() > 0 && !user.getPrimaryEmail().getEmailAddress().toLowerCase().startsWith("portaltest")) { + // membership of unallocated means user should no longer be allowed to log in - + // when school staff leave, they are allocated to this school prior to being + // actually deleted from the database + // + // exception is the test account, which has been assigned to all the 'schools' that aren't real + // schools (unallocated, home educated & LEA) + + throw new LoginException("Login Failed"); + } + + return user; + + } + +} Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolInformationProvider.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolInformationProvider.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolInformationProvider.java 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,230 @@ +package uk.gov.westsussex.atomwide; + +import java.sql.SQLException; +import java.util.Iterator; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import uk.gov.westsussex.atomwide.data.AtomwideSchoolDAO; +import uk.gov.westsussex.atomwide.data.AtomwideSchoolData; +import uk.gov.westsussex.atomwide.data.AtomwideUserDAO; +import uk.gov.westsussex.atomwide.data.AtomwideUserLite; +import uk.gov.westsussex.atomwide.mapping.CodeMapping; +import uk.gov.westsussex.wsgfl.establishment.EducationEstablishment; +import uk.gov.westsussex.wsgfl.establishment.EstablishmentInformationAdapter; +import uk.gov.westsussex.wsgfl.establishment.EstablishmentInformationProvider; + +import com.arsdigita.util.Cache; +import com.arsdigita.util.UncheckedWrapperException; + +/** + * + * Implementation of EstablishmentInformationProvider + * + * fallback for schools that are not in CED - asumption is that they are of type + * other establishments, as all schools SHOULD be found in CED, some other + * establishments that are in use in places (eg. Home Educated) are not + * + * Also used to retrieve chair of governors for any school if it is not populated in CED + * + * @author cgyg9330 + * + */ +public class AtomwideSchoolInformationProvider extends + EstablishmentInformationAdapter { + + private static Logger s_log = Logger + .getLogger(AtomwideSchoolInformationProvider.class); + + private Cache schools = new Cache(300, 0); + + private Cache cogs = new Cache(300, 0); + + private AtomwideSchoolData getData(String dfesID) throws SQLException { + AtomwideSchoolData data = (AtomwideSchoolData) schools.get(dfesID); + if (data == null) { + data = cacheSchoolData(dfesID); + } else { + s_log.debug("school data found in cache"); + } + return data; + } + + private String getCOG(String dfesID) throws SQLException { + s_log.debug("lookup chair of Govs for " + dfesID); + String data = (String) cogs.get(dfesID); + if (data == null) { + data = cacheCOG(dfesID); + } else { + s_log.debug("cog data found in cache"); + } + return data; + } + + public boolean exists(String id) { + String[] idParts = StringUtils.split(id, + EducationEstablishment.ID_SEPARATOR); + + boolean exists = false; + try { + AtomwideSchoolData data = getData(idParts[1]); + if (data != null) { + exists = true; + + } + + } catch (SQLException e) { + throw new UncheckedWrapperException( + "unable to retrieve school data from CED for school " + id, + e); + + } + return exists; + } + + private synchronized AtomwideSchoolData cacheSchoolData(String dfesID) + throws SQLException { + // check in cache again in case another thread added it while we were + // blocked + AtomwideSchoolData data = (AtomwideSchoolData) schools.get(dfesID); + if (data == null) { + s_log.debug("get school data and place in cache"); + data = getSchoolDataFromAtomwide(dfesID); + if (data != null) { + schools.put(dfesID, data); + s_log.debug("adding " + dfesID + " -> " + data.getName() + + " to cache"); + } else { + s_log.debug("establishment " + dfesID + + " not found in Atomwide"); + } + } + return data; + + } + + private AtomwideSchoolData getSchoolDataFromAtomwide(String dfesID) + throws SQLException { + return new AtomwideSchoolDAO().retrieve(dfesID); + + } + + private String getCOGFromAtomwide(String dfesID) throws SQLException { + Iterator users = new AtomwideUserDAO().findUsersInRole(dfesID, "R", 22, + false).iterator(); + // assume only one chair of governors + String name = null; + if (users.hasNext()) { + AtomwideUserLite chair = (AtomwideUserLite) users.next(); + name = chair.getFirstName() + " " + chair.getLastName(); + } + return name; + + } + + private synchronized String cacheCOG(String dfesID) throws SQLException { + // check in cache again in case another thread added it while we were + // blocked + String cog = (String) cogs.get(dfesID); + if (cog == null) { + s_log.debug("get cog and place in cache"); + cog = getCOGFromAtomwide(dfesID); + + if (cog != null) { + cogs.put(dfesID, cog); + s_log.debug("adding " + dfesID + " -> " + cog + " to cache"); + } else { + s_log.debug("Chair of Govs for " + dfesID + + " not found in Atomwide"); + } + } + return cog; + + } + + public String getName(String id, String context) { + String[] idParts = StringUtils.split(id, + EducationEstablishment.ID_SEPARATOR); + + String name = null; + try { + AtomwideSchoolData data = getData(idParts[1]); + if (data != null) { + name = data.getName(); + + } + + } catch (SQLException e) { + throw new UncheckedWrapperException( + "unable to retrieve school data from atomwide for school " + + id, e); + + } + return name; + } + + /** + * this provider should come after CED provider. Assumption here is that if + * establishment is in Atomwide but not CED then in must be of type other + */ + public String getType(String id, String context) { + CodeMapping other = CodeMapping.otherEstablishments(); + String type = null; + if (EstablishmentInformationProvider.CONTEXT_CODE.equals(context)) { + type = other.getCodeValue(); + } else { + type = other.getParentTerm().getName(); + } + + return type; + } + + public String getChairOfGovernors(String id, String context) { + String[] idParts = StringUtils.split(id, + EducationEstablishment.ID_SEPARATOR); + + String name = null; + try { + name = getCOG(idParts[1]); + } catch (SQLException e) { + throw new UncheckedWrapperException( + "unable to retrieve chair of goves from atomwide for school " + + id, e); + + } + return name; + } + + public int getCurrentHash(String id) { + String[] idParts = StringUtils.split(id, + EducationEstablishment.ID_SEPARATOR); + int hash = 0; + try { + + AtomwideSchoolData data = getSchoolDataFromAtomwide(idParts[1]); + String cog = getCOGFromAtomwide(idParts[1]); + hash = (data == null || data.getName() == null ? 0 : data.getName() + .hashCode()) + * 2 + (cog == null ? 0 : cog.hashCode()) * 3; + + } catch (SQLException e) { + throw new UncheckedWrapperException( + "unable to retrieve school data from atomwide for school " + + id, e); + + } + return hash; + + } + + public void clearCachedData(String id) { + s_log.debug("Removing establishment " + id + " from cache"); + String[] idParts = StringUtils.split(id, + EducationEstablishment.ID_SEPARATOR); + schools.remove(idParts[1]); + cogs.remove(idParts[1]); + + } + +} Added: aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolListener.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolListener.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-atomwide/src/uk/gov/westsussex/atomwide/AtomwideSchoolListener.java 2007-09-17 10:00:20 UTC (rev 1634) @@ -0,0 +1,188 @@ + +package uk.gov.westsussex.atomwide; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.atomwide.mapping.CodeMapping; + +import com.arsdigita.domain.DomainCollection; +import com.arsdigita.kernel.Group; +import com.arsdigita.london.terms.Domain; +import com.arsdigita.london.terms.Term; +import com.arsdigita.persistence.DataCollection; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.DataQuery; +import com.arsdigita.persistence.SessionManager; + +/** + * + * listener responds to changes in establishments. In particular, this listener + * needs to take care of groups associated with role groupings. + * + * If the way in which a school is classified changes, then the groups need to move within the group hierarchy. + * eg. + * + * - actual change - school changes from type other to type primary + * + * - new classification introduced (via command line program) eg by locality, by town etc + * + * - classification branch removed by command line program + * + * If a new school appears then we need a new group for each role grouping. If a school is + * removed, associetd groups must move. If a school is renamed then associated groups must be renamed. + * + * + * @author cgyg9330 + * + */ +public class AtomwideSchoolListener implements SchoolListener { + + public static final String GROUPS_IN_CATEGORY_QUERY = "uk.gov.westsussex.atomwide.GroupForCategories"; + + private static Logger s_log = Logger.getLogger(AtomwideSchoolListener.class); + + + public void newSchoolClassification(School school, CodeMapping mapper) { + s_log.debug("new school classification - add to role grouping group hierarchy"); + DomainCollection roleGroupings = + Domain + .retrieve(AtomwideConstants.ROLES_DOMAIN_KEY) + .getTerm(AtomwideConstants.ROLE_GROUPINGS_ID) + .getNarrowerTerms(); + // parent term means eg All Primary Schools term - the direct parent of individual school terms + Term parent = mapper.getParentTerm(); + s_log.debug("New parent term identified as " + parent.getName() + "(" + parent.getUniqueID() + ")"); + DataQuery groups = SessionManager.getSession().retrieveQuery(GROUPS_IN_CATEGORY_QUERY); + + // need to create new group for each role grouping + while (roleGroupings.next()) { + RoleGrouping grouping = new RoleGrouping((Term) roleGroupings.getDomainObject()); + s_log.debug("Role Grouping: " + grouping.getGroupingTerm().getName()); + // locate parent group that new group should appear under - eg All Primary SChools headteachers + // ie group associated with this role grouping AND appropriate school parent term + groups.setParameter("roleGrouping", grouping.getGroupingTerm().getModel().getID()); + groups.setParameter("school", parent.getModel().getID()); + while (groups.next()) { + Group group = new Group((DataObject) groups.get("group")); + s_log.debug("new parent group found: " + group.getName()); + // note createGroups doesn't create a new leaf group if it exists, + // but just wires up the existing school group to the new parent + grouping.createGroups(group, school.getSchoolTerm()); + } + // reuse the groups query for the next role grouping + groups.reset(); + } + } + + public void schoolClassificationRemoved(School school, Term parentTerm) { + s_log.debug("school classification removed: " + school.getName() + " from " + parentTerm.getName() ); + + // for each role grouping + // identify parent group eg all primary school headteachers + // identify school group eg bosham primary headteachers + // remove child/parent relationship + Group schoolGroup = null; + Group existingParentGroup = null; + DomainCollection roleGroupings = + Domain + .retrieve(AtomwideConstants.ROLES_DOMAIN_KEY) + .getTerm(AtomwideConstants.ROLE_GROUPINGS_ID) + .getNarrowerTerms(); + DataQuery groups = SessionManager.getSession().retrieveQuery(GROUPS_IN_CATEGORY_QUERY); + DataQuery parentGroups = SessionManager.getSession().retrieveQuery(GROUPS_IN_CATEGORY_QUERY); + + while (roleGroupings.next()) { + RoleGrouping grouping = new RoleGrouping((Term) roleGroupings.getDomainObject()); + s_log.debug("sort out groups for role grouping: " + grouping.getGroupingTerm().getName()); + groups.setParameter("roleGrouping", grouping.getGroupingTerm().getModel().getID()); + groups.setParameter("school", school.getSchoolTerm().getModel().getID()); + while (groups.next()) { + schoolGroup = new Group((DataObject) groups.get("group")); + + s_log.debug("found group for school under this role grouping " + schoolGroup.getName()); + } + groups.reset(); + parentGroups.setParameter("roleGrouping", grouping.getGroupingTerm().getModel().getID()); + parentGroups.setParameter("school", parentTerm.getModel().getID()); + while (parentGroups.next()) { + existingParentGroup = new Group((DataObject) parentGroups.get("group")); + s_log.debug("found existing parent group in this role grouping " + existingParentGroup.getName()); + } + s_log.debug("remove parent child relationship between " + existingParentGroup.getName() + " and " + schoolGroup.getName()); + existingParentGroup.removeSubgroup(schoolGroup); + parentGroups.reset(); + + } + } + + public void schoolCreated(School school) { + s_log.debug("New School created: " + school.getName() + " - create role grouping groups"); + DomainCollection roleGroupings = + Domain + .retrieve(AtomwideConstants.ROLES_DOMAIN_KEY) + .getTerm(AtomwideConstants.ROLE_GROUPINGS_ID) + .getNarrowerTerms(); + DomainCollection parents = school.getSchoolTerm().getBroaderTerms(); + DataQuery groups = SessionManager.getSession().retrieveQuery(GROUPS_IN_CATEGORY_QUERY); + + while (roleGroupings.next()) { + RoleGrouping grouping = new RoleGrouping((Term) roleGroupings.getDomainObject()); + s_log.debug("role grouping " + grouping.getGroupingTerm().getName()); + while (parents.next()) { + Term parent = (Term)parents.getDomainObject(); + s_log.debug("Parent term of school: " + parent.getName()); + groups.setParameter("roleGrouping", grouping.getGroupingTerm().getModel().getID()); + groups.setParameter("school", parent.getModel().getID()); + while (groups.next()) { + Group group = new Group((DataObject) groups.get("group")); + s_log.debug("parent group found " + group.getName()); + grouping.createGroups(group, school.getSchoolTerm()); + } + groups.reset(); + } + parents.rewind(); + } + + s_log.debug("END - createGroups"); + + + + } + + public void schoolRemoved(School school) { + s_log.debug("school removed from Atomwide DB: " + school.getName() + "(" + school.getDfESID() + ")"); + DataCollection groups = getSchoolGroups(school); + + Group group; + while (groups.next()) { + group = new Group(groups.getDataObject()); + s_log.debug("associated group found: " + group.getName()); + group.delete(); + } + } + + public v... [truncated message content] |
From: <cl...@fe...> - 2007-09-17 09:00:43
|
Author: clasohm Date: 2007-09-17 11:00:30 +0200 (Mon, 17 Sep 2007) New Revision: 1631 Added: aplaws/trunk/ccm-ldn-shortcuts/.classpath aplaws/trunk/ccm-ldn-shortcuts/.project Log: added Eclipse project files Added: aplaws/trunk/ccm-ldn-shortcuts/.classpath =================================================================== --- aplaws/trunk/ccm-ldn-shortcuts/.classpath (rev 0) +++ aplaws/trunk/ccm-ldn-shortcuts/.classpath 2007-09-17 09:00:30 UTC (rev 1631) @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="lib" path="build/ccm-ldn-shortcuts-6.5.0-pdl.jar"/> + <classpathentry kind="lib" path="build/ccm-ldn-shortcuts-6.5.0-sql.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/ccm-core"/> + <classpathentry kind="output" path="build/Eclipse"/> +</classpath> Added: aplaws/trunk/ccm-ldn-shortcuts/.project =================================================================== --- aplaws/trunk/ccm-ldn-shortcuts/.project (rev 0) +++ aplaws/trunk/ccm-ldn-shortcuts/.project 2007-09-17 09:00:30 UTC (rev 1631) @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>ccm-ldn-shortcuts</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> |
From: <chr...@fe...> - 2007-09-17 09:00:41
|
Author: chrisg23 Date: 2007-09-17 11:00:40 +0200 (Mon, 17 Sep 2007) New Revision: 1632 Added: aplaws/contrib/wsx/ccm-wsx-authentication/application.xml aplaws/contrib/wsx/ccm-wsx-authentication/pdl/ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.ora.pdl aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.pg.pdl aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/UserTracker.pdl aplaws/contrib/wsx/ccm-wsx-authentication/sql/ aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/ aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se-create.sql aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se/ aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se/remove_trailing_digits_function.sql aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/postgres-create.sql aplaws/contrib/wsx/ccm-wsx-authentication/src/ aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.config aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.load aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig.java aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig_parameter.properties aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserFactory.java aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserIdentifier.java aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Initializer.java aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/MultipleDirectoryLoginModule.java aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/UserTracker.java aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Utils.java Log: Authentication Module - allows authentication from multiple directories, and also allows tracking of user login events. To authenticate against the local Aplaws database of users, then against an LDAP Directory and finally against an Atomwide (school user) directory, the MultipleDirectoryLoginModule from this package must be included in the Register context, and contexts for the external directory must be named and defined eg waf.login_config=Request\:com.arsdigita.kernel.security.AdminLoginModule\:sufficient,Request\:com.arsdigita.kernel.security.RecoveryLoginModule\:sufficient,Request\:com.arsdigita.kernel.security.CookieLoginModule\:requisite,Register\:uk.gov.westsussex.authentication.MultipleDirectoryLoginModule\:requisite,Register\:com.arsdigita.kernel.security.CookieLoginModule\:optional,Local\:com.arsdigita.kernel.security.LocalLoginModule\:requisite,Local\:com.arsdigita.kernel.security.UserIDLoginModule\:requisite,LDAP\:uk.gov.westsussex.ldap.LDAPAuthenticationModule! \:requisite,LDAP\:uk.gov.westsussex.ldap.LDAPMappingLoginModule\:requisite,Atomwide\:uk.gov.westsussex.atomwide.AtomwideAuthenticationModule\:requisite,Atomwide\:uk.gov.westsussex.atomwide.AtomwideMappingLoginModule\:requisite The names given to the additional contexts - LDAP and Atomwide above are then referenced by a config parameter that determines which contexts to use when registering a user - uk.gov.westsussex.authentication.included_login_contexts=Local,LDAP,Atomwide. Currently each directory is checked in turn until the entered username is found and the password matches. It would be possible to extend the included_login_context parameter to take pairs of values - one being the context name and the other a regular expression defining the pattern of the ids used by that directory. Hence at login we could check only the directories that use the same pattern as that entered by the user. Added: aplaws/contrib/wsx/ccm-wsx-authentication/application.xml =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/application.xml (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/application.xml 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<ccm:application name="ccm-wsx-authentication" + prettyName="Multiple Directory Login Mechanism" + version="1.0.1" + release="1" + webapp="ROOT" + xmlns:ccm="http://ccm.redhat.com/ccm-project"> + + <ccm:dependencies> + <ccm:requires name="ccm-core" version="6.1.0"/> + </ccm:dependencies> + + <ccm:directories> + <ccm:directory name="pdl"/> + <ccm:directory name="src"/> + <ccm:directory name="sql"/> + </ccm:directories> + + <ccm:contacts> + <ccm:contact uri="http://www.redhat.com/software/ccm" type="website"/> + <ccm:contact uri="mailto:cc...@re..." type="support"/> + </ccm:contacts> + + <ccm:description> + Login Modules that enable login from several user directoriess. + </ccm:description> +</ccm:application> Added: aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.ora.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.ora.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.ora.pdl 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,15 @@ +model uk.gov.westsussex.authentication; + +query NameCount { + Integer count; + do { + select count(*) as count + from person_names + where lower(remove_trailing_digits(family_name)) = lower(:lastname) + and lower(given_name) = lower(:firstname) + } map { + count = count; + + } +} + Added: aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.pg.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.pg.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/Authentication.pg.pdl 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,18 @@ +model uk.gov.westsussex.authentication; +// doubt if the provided remove trailing digits function works in postgres +// as it relies on Oracle specific functions. So there is a need to write an +// equivalent + +query NameCount { + Integer count; + do { + select count(*) as count + from person_names + where lower(remove_trailing_digits(family_name)) = lower(:lastname) + and lower(given_name) = lower(:firstname) + } map { + count = count; + + } +} + Added: aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/UserTracker.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/UserTracker.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/pdl/uk/gov/westsussex/authentication/UserTracker.pdl 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,17 @@ +model uk.gov.westsussex.authentication; + +import com.arsdigita.kernel.User; + +// doesn't need to be an acs_object + +object type UserTracker { + + BigDecimal [1..1] userID = user_tracker.user_id; + Date [1..1] firstLoggedIn = user_tracker.first_logged_in DATE; + Date [1..1] lastLoggedIn = user_tracker.last_logged_in DATE; + Integer [1..1] loginCount = user_tracker.login_count INTEGER; + Boolean [1..1] trueFirstLog = user_tracker.true_first_log; + + object key (userID); + +} \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se/remove_trailing_digits_function.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se/remove_trailing_digits_function.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se/remove_trailing_digits_function.sql 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,10 @@ +CREATE OR REPLACE function remove_trailing_digits ( + p_word in varchar2 +) return varchar2 + is + +begin + return TRIM(trailing '9' from TRANSLATE(lower(p_word), '0123456789', '9999999999')); +end remove_trailing_digits; +/ + Added: aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/oracle-se-create.sql 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1 @@ +@ ddl/oracle-se/create.sql Added: aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/postgres-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/postgres-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/sql/ccm-wsx-authentication/postgres-create.sql 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,3 @@ +begin; +\i ddl/postgres/create.sql +end; Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.config =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.config (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.config 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<registry> + <config class="uk.gov.westsussex.authentication.AuthenticationConfig" + storage="ccm-wsx-authentication/authentication.properties"/> +</registry> Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.load =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.load (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/ccm-wsx-authentication.load 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,13 @@ +<load> + <requires> + <table name="inits"/> + <initializer class="com.arsdigita.core.Initializer"/> + </requires> + <provides> + <initializer class="uk.gov.westsussex.authentication.Initializer"/> + </provides> + <scripts> + <schema directory="ccm-wsx-authentication"/> + </scripts> + +</load> Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig.java 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.authentication; + +import com.arsdigita.runtime.AbstractConfig; +import com.arsdigita.util.parameter.BooleanParameter; +import com.arsdigita.util.parameter.Parameter; +import com.arsdigita.util.parameter.StringArrayParameter; + +/** + * Enable different instances of Aplaws to switch login + * contexts on or off. + * + * @author Chris Gilbert <chr...@we...> + * @version $Id: AuthenticationConfig.java,v 1.4 2007/08/08 09:19:26 cgyg9330 Exp $ + */ +public class AuthenticationConfig extends AbstractConfig { + public final static String versionId = + "$Id: AuthenticationConfig.java,v 1.4 2007/08/08 09:19:26 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/18 11:10:20 $"; + + + private Parameter includedLoginContexts; + private Parameter uniqueNames; + + public AuthenticationConfig() { + + includedLoginContexts = new StringArrayParameter( + "uk.gov.westsussex.authentication.included_login_contexts", + Parameter.REQUIRED, + new String[]{"Local", "Ldap"}); + uniqueNames = new BooleanParameter( + "uk.gov.westsussex.authentication.unique_names", + Parameter.REQUIRED, + Boolean.TRUE); + register(includedLoginContexts); + register(uniqueNames); + loadInfo(); + } + + + /** + * returns an array of contexts to be invoked in the order they appear. Context names must be ones that + * have been specified in the waf.login_config parameter that specifies the JAAS stack + * @return + */ + public final String[] getIncludedLoginContexts() { + return (String[])get(includedLoginContexts); + } + /** + * Returns the value of the unique names configuration parameter. + * If true, the Utils class setCCMName utility will append duplicate names with a digit to ensure uniqueness. + * NB. Uniqueness only applies to new names - if there are existing duplicates they will remain. + * + * @return + */ + public boolean ensureUniqueNames() { + return ((Boolean)get(uniqueNames)).booleanValue(); + } + + +} Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig_parameter.properties =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig_parameter.properties (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/AuthenticationConfig_parameter.properties 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,9 @@ +uk.gov.westsussex.authentication.included_login_contexts.title=Included Login Contexts +uk.gov.westsussex.authentication.included_login_contexts.purpose=Defines which contexts to use during login and the order in which they are checked. Contexts must be defined in ccm-core src/com/arsdigita/core/enterprise.init +uk.gov.westsussex.authentication.included_login_contexts.example=Local,Ldap +uk.gov.westsussex.authentication.included_login_contexts.format=[string,string,string] + +uk.gov.westsussex.authentication.unique_names.title=Ensure Unique Names +uk.gov.westsussex.authentication.unique_names.purpose=Ensure uniqueness of names if required, by appending numbers onto family names where necessary +uk.gov.westsussex.authentication.unique_names.format=[boolean] +uk.gov.westsussex.authentication.unique_names.example=true|false Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserFactory.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserFactory.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserFactory.java 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,62 @@ +/* + * Created on 28-Feb-05 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.authentication; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +import com.arsdigita.kernel.User; + +/** + * @author cgyg9330 + * + * Retrieves instance of appropriate User wrapper class for a user. + * Algorithm assumes that a user can only be one type of external user + * ie. External User types should map directly to external user directories + * Each external directory representation that wishes to provide additional user services should register an implementation of + * the ExternalUserIdentifier interface and provide a custom user class that imlpements interfaces that + * describe the additional services + * + * @see uk.gov.westsussex.authentication.ExternalUserIdentifier + */ +public class ExternalUserFactory { + + private static Collection userTypeIdentifiers = new HashSet(); + + /** + * registers a means of identifying a user + * @param identifier + */ + public static void registerExternalUserIdentifier(ExternalUserIdentifier identifier) { + userTypeIdentifiers.add(identifier); + } + + /** + * if the user originated in a directory that has registered an identifier + * returns an instance of an appropriate external user class for the user + * else just return the user + * + * @param user + * @return + */ + public static Object getCustomUserObject(User user) { + Iterator it = userTypeIdentifiers.iterator(); + while (it.hasNext()) { + ExternalUserIdentifier identifier = + (ExternalUserIdentifier) it.next(); + if (identifier.knowsThisUser(user)) { + return identifier.getCustomUser(user); + } + + } + return user; + + } + + +} \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserIdentifier.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserIdentifier.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/ExternalUserIdentifier.java 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,60 @@ +package uk.gov.westsussex.authentication; + +import com.arsdigita.kernel.User; + +/** + * @author chr...@we... + * + * Authentication modules should provide a concrete implementation of this class only if they provide + * specific services over and above those available from com.arsdigita.kernel.User + * + * Any such services should be encapsulated in an interface that may be implemented by a custom user object. + * eg uk.gov.westsussex.authentication.personalisation.PersonalisedNewsTarget + * This custom user object is returned by the getCustomUser method. Any class wishing to make use + * of services should check if the customUser is an instance of the required interface. + * + */ + public abstract class ExternalUserIdentifier { + + /** + * + * Determine if the user was authenticated by the implementing authentication module. + * + * Rather than querying the external directory, it is more efficient if + * the stratgegy can look at some attribute of the user that identifies their origin + * for example the format of the users email address + * + * eg + * + * <pre> + * public boolean knowsThisUser(User user) { + * String email = user.getPrimaryEmail().getEmailAddress(); + * return email.toLowerCase().endsWith(ATOMWIDE_MAIL_DOMAIN); + * } + * </pre> + * + */ + public abstract boolean knowsThisUser(User user); + + + /** + * Authentication modules may wish to provide a wrapper class for users that implements personalisation services. + * If so, this should be returned by overriding the getCustomUser method.Applications that use personalisation may + * look for customUser types that implement specific interfaces eg.in a news portlet + *<pre> + * User thisUser =(User)Kernel.getContext().getParty(); + * Object customUser = ExternalUserFactory.getCustomUserObject(thisUser); + * if (customUser instanceof PersonalisedNewsTarget) { + * getPersonalisedNews(state,newsPortlet,(PersonalisedNewsTarget) customUser); + * } + * + */ + + public Object getCustomUser(User user) { + return user; + } + + + + +} Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Initializer.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Initializer.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Initializer.java 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2001, 2002, 2003 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.authentication; + +import com.arsdigita.db.DbHelper; +import com.arsdigita.domain.DomainObject; +import com.arsdigita.domain.DomainObjectInstantiator; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.pdl.ManifestSource; +import com.arsdigita.persistence.pdl.NameFilter; +import com.arsdigita.runtime.CompoundInitializer; +import com.arsdigita.runtime.DomainInitEvent; +import com.arsdigita.runtime.PDLInitializer; +import com.arsdigita.runtime.RuntimeConfig; + +/** + * Basic Initializer - only used for boilerplate persistence + * code relating to the UserTracker domain object + * + * */ +public class Initializer extends CompoundInitializer { + public final static String versionId = + "$Id: Initializer.java,v 1.4 2007/08/08 09:19:26 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/18 14:28:05 $"; + + + public Initializer() { + final String url = RuntimeConfig.getConfig().getJDBCURL(); + final int database = DbHelper.getDatabaseFromURL(url); + + add( + new PDLInitializer( + new ManifestSource( + "ccm-wsx-authentication.pdl.mf", + new NameFilter( + DbHelper.getDatabaseSuffix(database), + "pdl")))); + } + + public void init(DomainInitEvent event) { + super.init(event); + + event + .getFactory() + .registerInstantiator( + UserTracker.BASE_DATA_OBJECT_TYPE, + new DomainObjectInstantiator() { + protected DomainObject doNewInstance(DataObject dataObject) { + return new UserTracker(dataObject); + } + + }); + + } +} Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/MultipleDirectoryLoginModule.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/MultipleDirectoryLoginModule.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/MultipleDirectoryLoginModule.java 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,216 @@ +/* + * Created on 02-Oct-03 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.authentication; + +import java.util.Iterator; +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.User; +import com.arsdigita.kernel.security.LoginContext; +import com.arsdigita.kernel.security.PartyPrincipal; + +/** + * @author chr...@we...s + * + * To use this module, it must be specified in the Register login context. + * + * This module retrieves a list of login contexts from uk.gov.westsussex.authentication.included_login_contexts + * configuration parameter and invokes login on each named context. DON'T specify this module in the list - I haven't tried it, but + * I imagine an endless loop will be the result. The order of contexts in uk.gov.westsussex.authentication.included_login_contexts + * determines the order in which external directories will be tried + * + * Each context MUST have a corresponding entry in the waf.login.config parameter (although it is + * valid to have contexts defined in waf.login.config that are not specified in + * uk.gov.westsussex.authentication.included_login_contexts - they are simply not used + * + * This module will stop as soon as a context succeeds. Each loginContext may have any arrangement + * of modules according to the JAAS model. Contexts were previously defined in the following format in + * enterprise.init.in + * + * "Register", { + * {"uk.gov.westsussex.authentication.MultipleDirectoryLoginModule", "requisite"}, + * {"com.arsdigita.kernel.security.CookieLoginModule", "optional"} + * }, + * "LDAP", { + * {"uk.gov.westsussex.ldap.LDAPAuthenticationModule", "requisite"}, + * {"uk.gov.westsussex.ldap.LDAPMappingLoginModule", "requisite"} + * }, + * "Atomwide", { + * {"uk.gov.westsussex.atomwide.AtomwideAuthenticationMudule", "requisite"}, + * {"uk.gov.westsussex.atomwide.AtomwideMappingLoginModule", "requisite"} + * } + * + * + * They are now specified in the uglier, but more useful format + * + * waf.login_config=Request\:com.arsdigita.kernel.security.AdminLoginModule\:sufficient,Request\:com.arsdigita.kernel.security.RecoveryLoginModule\:sufficient, + * Request\:com.arsdigita.kernel.security.CookieLoginModule\:requisite,Register\:uk.gov.westsussex.authentication.MultipleDirectoryLoginModule\:requisite, + * Register\:com.arsdigita.kernel.security.CookieLoginModule\:optional,Local\:com.arsdigita.kernel.security.LocalLoginModule\:requisite, + * Local\:com.arsdigita.kernel.security.UserIDLoginModule\:requisite,LDAP\:uk.gov.westsussex.ldap.LDAPAuthenticationModule\:requisite, + * LDAP\:uk.gov.westsussex.ldap.LDAPMappingLoginModule\:requisite,Atomwide\:uk.gov.westsussex.atomwide.AtomwideAuthenticationModule\:requisite + * ,Atomwide\:uk.gov.westsussex.atomwide.AtomwideMappingLoginModule\:requisite + + * + */ +public class MultipleDirectoryLoginModule implements LoginModule { + + /** + * used as a key in shared space to identify users who are logging in for the + * first time - neccessary because tracking has been introduced after + * external directory login. We need to flag user stats where the user has + * been logging in for a while as opposed to users who are genuinely + * logging in for the first time + */ + public static final String NEW_USER = "newUser"; + + private static AuthenticationConfig s_config = new AuthenticationConfig(); + private static String[] loginContexts; + + static { + s_config.load(); + loginContexts = s_config.getIncludedLoginContexts(); + + } + + public static AuthenticationConfig getConfig() { + return s_config; + } + + + private static final Logger s_log = + Logger.getLogger(MultipleDirectoryLoginModule.class); + + // fields set by initialize() + private Subject m_subject; + private CallbackHandler m_handler; + + // implements LoginModule + public void initialize( + Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + m_handler = handler; + m_subject = subject; + + + } + + /** + * + */ + + /** + * login invokes login on subcontexts until + * one succeeds. If they all fail, LoginException is throws + * @see javax.security.auth.spi.LoginModule#login() + * + * + * TODO - currently gives different messages depending if the error is in + * username or password - should be amended to give the same message eg + * 'Login Failed' BUT - to debug login problems for users, it would be a good idea + * to output the nature of the problem (password or user) at INFO level so + * logging level can temporarily be lowered for this class to check. + * + * + */ + public boolean login() throws LoginException { + s_log.debug("START - login"); + LoginContext context; + for (int i = 0; i < loginContexts.length; i++) { + context = new LoginContext(loginContexts[i], m_subject, m_handler); + try { + context.login(); + // success if no exception - no need to check any more contexts + return true; + } catch (LoginException e) { + s_log.debug(context.toString() + " login failed " + e); + if (e.getClass().equals(FailedLoginException.class)) { + // user found, but password doesn't match. Don't check any more contexts + + throw new LoginException("Incorrect password"); + } + } + } + + // all login modules have failed - user doesn't exist + throw new LoginException("User ID could not be found"); + + } + + /** + * commit is invoked if one of the contexts commits, in which case + * Subject will be populated by the successful context & Principal will have been added to m_subject by + * the successful context + */ + public boolean commit() throws LoginException { + s_log.debug("commit"); + + Iterator principals = m_subject.getPrincipals().iterator(); + try { + + while (principals.hasNext()) { + s_log.debug("principal found in shared space"); + Object principal = principals.next(); + if (principal instanceof PartyPrincipal) { + s_log.debug("principal instance of PartyPrincipal"); + User user = User.retrieve(((PartyPrincipal)principal).getID()); + UserTracker.login(user); + } + + } + } catch (Exception e) { + // if user tracking fails, don't stop user logging in + s_log.error("Couldn't track user login event", e); + } + return true; + } + + /** + * abort may be invoked if this module succeeded but another module in the same context failed. + * If this happens, then one of the sub-contexts may have committed, so this abort + * will remove any effects from the Subject + */ + public boolean abort() throws LoginException { + s_log.debug("abort"); + m_subject.getPrincipals().clear(); + return true; + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#logout() + */ + public boolean logout() throws LoginException { + + s_log.debug("START - logout"); + LoginContext context; + for (int i = 0; i < loginContexts.length; i++) { + context = new LoginContext(loginContexts[i], m_subject, m_handler); + + try { + context.logout(); + return true; + } catch (LoginException e) { + // exception in one login context doesn't matter - only an issue if all contexts fail + } + + } + throw new LoginException("Logout failed in all contexts"); + + } + + + +} Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/UserTracker.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/UserTracker.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/UserTracker.java 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,111 @@ +package uk.gov.westsussex.authentication; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.domain.DomainObject; +import com.arsdigita.kernel.User; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.OID; + +/** + * @author chr...@we... + * + * Domain object that holds information to allow us to determine when and + * how often a user logs in + */ +public class UserTracker extends DomainObject { + + public static final String USER_ID = "userID"; + public static final String FIRST_LOGGED_IN = "firstLoggedIn"; + public static final String LAST_LOGGED_IN = "lastLoggedIn"; + public static final String LOGIN_COUNT = "loginCount"; + public static final String FIRST_LOG_FLAG = "trueFirstLog"; + + public static final String BASE_DATA_OBJECT_TYPE = + "uk.gov.westsussex.authentication.UserTracker"; + + private static Set newUsers = new HashSet(); + + public UserTracker(DataObject object) { + super(object); + + } + + public UserTracker(OID oid) throws DataObjectNotFoundException { + super(oid); + } + + // create a new tracker record + public UserTracker (User user) { + super(BASE_DATA_OBJECT_TYPE); + set (USER_ID, user.getID()); + set (FIRST_LOGGED_IN, new Date()); + set (LAST_LOGGED_IN, new Date()); + set (LOGIN_COUNT, new Integer(1)); + // new tracker record AND a new ccm record is being created, therefore this is a real + // first login + set (FIRST_LOG_FLAG, new Boolean(newUsers.contains(user.getID()))); + newUsers.remove(user.getID()); + } + + public static void login(User user) { + UserTracker tracker; + try { + tracker = UserTracker.retrieve(user); + tracker.set (LAST_LOGGED_IN, new Date()); + int loginCount = ((Integer)tracker.get(LOGIN_COUNT)).intValue(); + tracker.set (LOGIN_COUNT, new Integer(++loginCount)); + } catch (DataObjectNotFoundException e) { + // new user + tracker = new UserTracker(user); + + + } + + } + + /** + * used by login modules to record when a user is genuinely logging in for + * the first time. This would not be required for a brand new install + * of Aplaws, but is needed if users have been logging in for some time + * before tracking is implemented. + * + * An example of usage is + * + * <pre> + * + * UserCollection users = User.retrieveAll(); + * users.addEqualsFilter( + * "primaryEmail", + * ((String) ldapUser.getMail().get(0))); + * try { + * if (users.next()) { + * s_log.debug("retrieving existing ccm user"); + * ccmUser = users.getUser(); + * + * } else { + * s_log.debug("creating new ccm user"); + * ccmUser = new User(); + * UserTracker.setNew(ccmUser); + * } + * + * </pre> + * + * @param user + */ + public static synchronized void setNew (User user) { + newUsers.add(user.getID()); + } + + public static UserTracker retrieve (User user) throws DataObjectNotFoundException { + return new UserTracker(new OID(BASE_DATA_OBJECT_TYPE, user.getID())); + + + } + + + +} Added: aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Utils.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Utils.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-authentication/src/uk/gov/westsussex/authentication/Utils.java 2007-09-17 09:00:40 UTC (rev 1632) @@ -0,0 +1,92 @@ +/* + * Created on 29-Jun-04 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.authentication; + +import com.arsdigita.kernel.PersonName; +import com.arsdigita.persistence.DataQuery; +import com.arsdigita.persistence.Session; +import com.arsdigita.persistence.SessionManager; + +/** + * @author cgyg9330 + * + * Utilities required during the login and mapping process + * + */ +public class Utils { + + private static final String NAME_COUNT_QUERY = + "uk.gov.westsussex.authentication.NameCount"; + private static final String FIRST_NAME_PARAMETER = "firstname"; + private static final String LAST_NAME_PARAMETER = "lastname"; + private static final String QUERY_RETURN_VALUE = "count"; + + /** + * + * @param ccmName the PersonName Object that is to be set/updated + * @param firstName the firstname retreived from user directory + * @param lastName the lastname retreived from the user directory + * Set/update the com.arsdigita.kernel.PersonName object associated + * with a ccm user based on the current name retrieved from the user directory. + * Directory users are mapped to ccm users by email address at login. + * + * In order to comply with restrictions on some external software, user names (first name + * and last name concatenated) need to be unique. In order to manage this, any names that already + * exist are appended with a number. + * + */ + public static void setCCMName( + PersonName ccmName, + String firstName, + String lastName) { + // is the name unchanged? + // nb - if this is a new ccm user, ccmName will have null attributes + if (ccmName.getGivenName() != null + && ccmName.getGivenName().equals(firstName) + && ccmName.getFamilyName() != null + && stripTrailingDigits(ccmName.getFamilyName()).equals(lastName)) { + return; + } + if (MultipleDirectoryLoginModule.getConfig().ensureUniqueNames()) { + + //has anyone else got the same name? + Session session = SessionManager.getSession(); + DataQuery getCount = session.retrieveQuery(NAME_COUNT_QUERY); + getCount.setParameter(FIRST_NAME_PARAMETER, firstName); + getCount.setParameter(LAST_NAME_PARAMETER, lastName); + + if (getCount.next()) { + int existingNames = + ((Integer) getCount.get(QUERY_RETURN_VALUE)).intValue(); + if (existingNames > 0) { + // append name with a digit 1 greater than any existing apended names + // (nb there is nobody with 1 appended, appended digits start at 2) + lastName = lastName + (existingNames + 1); + } + + } getCount.close(); + } + ccmName.setGivenName(firstName); + ccmName.setFamilyName(lastName); + + } + + //NB will actually strip away any digits. I am assuming that the only + //digits in the name are ones that have been added at the end + //(either by me or in the users directory entry) + private static String stripTrailingDigits(String lastName) { + StringBuffer strippedName = new StringBuffer(); + char[] nameCharacters = lastName.toCharArray(); + for (int i = 0; i < nameCharacters.length; i++) { + if (!Character.isDigit(nameCharacters[i])) { + strippedName.append(nameCharacters[i]); + } + } + return strippedName.toString(); + } + +} |
From: <chr...@fe...> - 2007-09-17 09:00:40
|
Author: chrisg23 Date: 2007-09-17 11:00:42 +0200 (Mon, 17 Sep 2007) New Revision: 1633 Added: aplaws/contrib/wsx/ccm-wsx-ldap/application.xml aplaws/contrib/wsx/ccm-wsx-ldap/pdl/ aplaws/contrib/wsx/ccm-wsx-ldap/pdl/uk/ aplaws/contrib/wsx/ccm-wsx-ldap/pdl/uk/gov/ aplaws/contrib/wsx/ccm-wsx-ldap/pdl/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-ldap/pdl/uk/gov/westsussex/LDAP.pdl aplaws/contrib/wsx/ccm-wsx-ldap/sql/ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/oracle-se-create.sql aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/postgres-create.sql aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/default/ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/default/1.0.3-1.0.4/ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/default/1.0.3-1.0.4/add-temp_id_track_table.sql aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/oracle-se-1.0.3-1.0.4.sql aplaws/contrib/wsx/ccm-wsx-ldap/src/ aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.config aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.load aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.upgrade aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Initializer.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAP.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPAuthenticationModule.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig_parameter.properties aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConnection.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConstants.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPDirectory.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPMappingLoginModule.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPUser.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Loader.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TempIDTracker.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TrackTempIDScheduler.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TrackTempIDTask.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/mapping/ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/mapping/GroupMapper.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/mapping/LDAPAttributeMapper.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/mapping/LDAPMapper.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/mapping/LDAPProfileMapper.java aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/upgrade/ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/upgrade/RemoveCountyStaffMembers.java Log: LDAP Authentication Module. At the moment, only a few connection attributes may be specified as config parameters. A better approach would be to specify a properties file in config that is used for the LDAP connection. This module is dependent on ccm-wsx-authentication because it does refer to user tracking (in order to flag users who have logged in prior to implementation of tracking and so aren't new users, but who are having a login event recorded for the first time Added: aplaws/contrib/wsx/ccm-wsx-ldap/application.xml =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/application.xml (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/application.xml 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<ccm:application name="ccm-wsx-ldap" + prettyName="LDAP Authentication Modules" + version="1.0.4" + release="1" + webapp="ROOT" + xmlns:ccm="http://ccm.redhat.com/ccm-project"> + + <ccm:dependencies> + <ccm:requires name="ccm-wsx-authentication" version="1.0.1"/> + <ccm:requires name="ccm-ldn-terms" version="1.0.1"/> + </ccm:dependencies> + + <ccm:directories> + <ccm:directory name="src"/> + <ccm:directory name="pdl"/> + <ccm:directory name="sql"/> + </ccm:directories> + + <ccm:contacts> + <ccm:contact uri="http://www.redhat.com/software/ccm" type="website"/> + <ccm:contact uri="mailto:cc...@re..." type="support"/> + </ccm:contacts> + + <ccm:description> + Modules to allow user authentication from an LDAP directory. + version 1.0.2 has added general directory search + </ccm:description> +</ccm:application> Added: aplaws/contrib/wsx/ccm-wsx-ldap/pdl/uk/gov/westsussex/LDAP.pdl =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/pdl/uk/gov/westsussex/LDAP.pdl (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/pdl/uk/gov/westsussex/LDAP.pdl 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,147 @@ +model uk.gov.westsussex; + +import com.arsdigita.web.Application; + + +object type LDAP extends Application { + +} + +object type LDAPTempIDTracker { + + String [1..1] uid = ldap_temp_id.ldap_uid VARCHAR(255); + String [0..1] email = ldap_temp_id.mail VARCHAR(255); + String [0..1] firstName = ldap_temp_id.first_name VARCHAR(255); + String [0..1] lastName = ldap_temp_id.last_name VARCHAR(255); + String [0..1] application = ldap_temp_id.application VARCHAR(255); + + object key (uid); + + +} + + +query LDAPNewTempIDsLocality { + + + String uid; + + options { + WRAP_QUERIES = false; + } + + do { + select distinct ldap_uid + from locality_contacts x + where lower(ldap_uid) like 'wscc%' + and not exists (select 1 from ldap_temp_id a where a.ldap_uid = x.ldap_uid) + + + + } map { + uid = ldap_uid; + } +} + +query LDAPNewTempIDsCorrespondence { + + + String uid; + + options { + WRAP_QUERIES = false; + } + + do { + select distinct network_id + from ct_correspondence_contacts y + where lower(network_id) like 'wscc%' + and not exists (select 1 from ldap_temp_id a where a.ldap_uid = y.network_id) + + + } map { + uid = network_id; + } +} + +query LDAPNewTempIDsSchoolVisits { + + + String uid; + + options { + WRAP_QUERIES = false; + } + + do { + select distinct ldap_uid + from school_visit_wscc_visitor y + where lower(ldap_uid) like 'wscc%' + and not exists (select 1 from ldap_temp_id a where a.ldap_uid = y.ldap_uid) + + + } map { + uid = ldap_uid; + } +} +query LDAPNewTempIDsCalendar { + + + String uid; + + options { + WRAP_QUERIES = false; + } + + do { + select distinct external_id + from cal_ext_contacts z + where directory_access_class = 'ldap' + and lower(external_id) like 'wscc%' + and not exists (select 1 from ldap_temp_id a where a.ldap_uid = z.external_id) + + + } map { + uid = external_id; + } +} + +query LDAPOldTempIDs { + + + String uid; + + options { + WRAP_QUERIES = false; + } + + do { + select ldap_uid + from ldap_temp_id a + where a.ldap_uid not in ( + select ldap_uid + from locality_contacts + where lower(ldap_uid) like 'wscc%' + union + select ldap_uid + from school_visit_wscc_visitor + where lower(ldap_uid) like 'wscc%' + union + select external_id + from cal_ext_contacts z + where directory_access_class = 'ldap' + and lower(external_id) like 'wscc%' + union + select network_id + from ct_correspondence_contacts + where lower(network_id) like 'wscc%') + + + + } map { + uid = ldap_uid; + } +} + + + Added: aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/oracle-se-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/oracle-se-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/oracle-se-create.sql 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,3 @@ +@ ddl/oracle-se/create.sql +@ ddl/oracle-se/deferred.sql + Added: aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/postgres-create.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/postgres-create.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/postgres-create.sql 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,4 @@ +begin; +\i ddl/postgres/create.sql +\i ddl/postgres/deferred.sql +end; Added: aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/default/1.0.3-1.0.4/add-temp_id_track_table.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/default/1.0.3-1.0.4/add-temp_id_track_table.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/default/1.0.3-1.0.4/add-temp_id_track_table.sql 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,8 @@ +create table ldap_temp_id ( + ldap_uid VARCHAR(255) not null + constraint ldap_temp_id_uid_p_zyahy + primary key, + mail VARCHAR(255), + first_name VARCHAR(255), + last_Name VARCHAR(255) +); Added: aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/oracle-se-1.0.3-1.0.4.sql =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/oracle-se-1.0.3-1.0.4.sql (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/sql/ccm-wsx-ldap/upgrade/oracle-se-1.0.3-1.0.4.sql 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1 @@ +@@ ../upgrade/default/1.0.3-1.0.4/add-temp_id_track_table.sql \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.config =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.config (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.config 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<registry> + <config class="uk.gov.westsussex.ldap.LDAPConfig" + storage="ccm-wsx-ldap/ldap.properties"/> +</registry> Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.load =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.load (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.load 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,14 @@ +<load> + <requires> + <table name="inits"/> + <table name="acs_objects"/> + <initializer class="com.arsdigita.core.Initializer"/> + + </requires> + <provides> + <initializer class="uk.gov.westsussex.ldap.Initializer"/> + </provides> + <scripts> + <data class="uk.gov.westsussex.ldap.Loader"/> + </scripts> +</load> Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.upgrade =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.upgrade (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/ccm-wsx-ldap.upgrade 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,8 @@ +<upgrade> + <version from="1.0.2" to="1.0.3"> + <script class="uk.gov.westsussex.ldap.upgrade.RemoveCountyStaffMembers"/> + </version> + <version from="1.0.3" to="1.0.4"> + <script sql="ccm-wsx-ldap/upgrade/::database::-1.0.3-1.0.4.sql"/> + </version> +</upgrade> Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Initializer.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Initializer.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Initializer.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,93 @@ +package uk.gov.westsussex.ldap; + +import org.apache.log4j.Logger; + +import com.arsdigita.db.DbHelper; +import com.arsdigita.domain.DomainObject; +import com.arsdigita.domain.DomainObjectInstantiator; +import com.arsdigita.kernel.ACSObjectInstantiator; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.pdl.ManifestSource; +import com.arsdigita.persistence.pdl.NameFilter; +import com.arsdigita.runtime.CompoundInitializer; +import com.arsdigita.runtime.DomainInitEvent; +import com.arsdigita.runtime.LegacyInitEvent; +import com.arsdigita.runtime.PDLInitializer; +import com.arsdigita.runtime.RuntimeConfig; + +/** + * @author cgyg9330 + * + * Ensures Aplaws personalisation categories tally with current Atomwide profile categories + * and caches a few bits for quick lokkup during user login + */ +public class Initializer + extends CompoundInitializer{ + public final static String versionId = + "$Id: Initializer.java,v 1.2 2007/07/31 13:16:44 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/18 14:28:05 $"; + + private static final Logger s_log = Logger.getLogger(Initializer.class); + + public Initializer() { + final String url = RuntimeConfig.getConfig().getJDBCURL(); + final int database = DbHelper.getDatabaseFromURL(url); + + add( + new PDLInitializer( + new ManifestSource( + "ccm-wsx-ldap.pdl.mf", + new NameFilter( + DbHelper.getDatabaseSuffix(database), + "pdl")))); + + } + + public void init(DomainInitEvent event) { + super.init(event); + + event + .getFactory() + .registerInstantiator( + LDAP.BASE_DATA_OBJECT_TYPE, + new ACSObjectInstantiator() { + public DomainObject doNewInstance(DataObject dataObject) { + return new LDAP(dataObject); + } + }); + event + .getFactory() + .registerInstantiator( + TempIDTracker.BASE_DATA_OBJECT_TYPE, + new DomainObjectInstantiator() { + public DomainObject doNewInstance(DataObject dataObject) { + return new TempIDTracker(dataObject); + } + }); +/* + // register strategy class for identifying school users + ExternalUserFactory.registerExternalUserIdentifier( + new AtomwideUserIdentifier()); +*/ + LDAP.getConfig().loadProfileMappers(); + + + } + + public final void init(final LegacyInitEvent e) { + super.init(e); + + if (RuntimeConfig.getConfig().runBackGroundTasks()) { + TrackTempIDScheduler.startTimer(); + } + + + + + } + + + + +} \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAP.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAP.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAP.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.ldap; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.Group; +import com.arsdigita.kernel.GroupCollection; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.OID; +import com.arsdigita.web.Application; + +public class LDAP extends Application { + private static LDAP application = null; + + private static Logger s_log = Logger.getLogger(LDAP.class); + private static Group allStaff = null; + + public static final String BASE_DATA_OBJECT_TYPE = "uk.gov.westsussex.LDAP"; + + private static LDAPConfig s_config = new LDAPConfig(); + + static { + s_config.load(); + } + + public static LDAPConfig getConfig() { + return s_config; + } + public LDAP(DataObject obj) { + super(obj); + } + + public LDAP(OID oid) { + super(oid); + } + + public static LDAP getApplication() { + if (application == null) { + + application = + (LDAP) Application.retrieveApplicationForPath("/ldap"); + s_log.debug("application retrieved " + application); + + } + return application; + } + + public String getServletPath() { + return "/ccm-wsx-ldap/files"; + } + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPAuthenticationModule.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPAuthenticationModule.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPAuthenticationModule.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,91 @@ +/* + * Created on 30-Sep-03 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.ldap; + +import java.util.Map; + +import javax.naming.NamingException; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; +import javax.security.auth.login.FailedLoginException; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.security.AccountNotFoundException; +import com.arsdigita.kernel.security.PasswordLoginModule; + +/** + * @author cgyg9330 + * + * Authenticates user against an LDAP directory. No action on success - JAAS just proceeds to the next loginmodule + */ +public class LDAPAuthenticationModule extends PasswordLoginModule { + + public static final String LDAP_USER = "ldapUser"; + + private static final Logger s_log = + Logger.getLogger(LDAPAuthenticationModule.class); + + private Subject m_subject; + private CallbackHandler m_handler; + private Map m_shared; + private Map m_options; + + // implements LoginModule + public void initialize( + Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + super.initialize(subject, handler, shared, options); + m_subject = subject; + m_handler = handler; + m_shared = shared; + m_options = options; + + } + + /** + * @see com.arsdigita.kernel.security.PasswordLoginModule#checkPassword(String, char[]) + */ + protected void checkPassword(String userID, char[] password) + throws LoginException { + s_log.debug("START - checkPassword"); + try { + LDAPConnection conn = LDAPConnection.getInstance(); + LDAPUser user = conn.getLDAPUser(userID); + + boolean success = user.authenticate(new String(password)); + if (!success) { + throw new FailedLoginException(); + } + s_log.debug("Authentication succeeded"); + // cache the user in shared space to save looking up again in the mapping module + m_shared.put(LDAP_USER, user); + + } catch (NamingException e) { + s_log.error("An LDAP error occurred", e); + throw new LoginException("An LDAP error occurred"); + } catch (AccountNotFoundException e) { + throw new LoginException("User ID " + userID + " could not be found"); + } + } + + public boolean commit() throws LoginException { + return false; + } + + public boolean abort() throws LoginException { + return false; + } + + public boolean logout() throws LoginException { + return false; + } + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ + +package uk.gov.westsussex.ldap; + +import com.arsdigita.runtime.AbstractConfig; +import com.arsdigita.util.parameter.Parameter; +import com.arsdigita.util.parameter.StringArrayParameter; +import com.arsdigita.util.parameter.URLParameter; +import com.arsdigita.util.parameter.StringParameter; +import com.arsdigita.util.UncheckedWrapperException; + +import com.arsdigita.persistence.Session; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.kernel.Group; +import com.arsdigita.kernel.GroupCollection; +import com.arsdigita.kernel.UserCollection; +import com.arsdigita.kernel.User; +import com.arsdigita.web.Web; + +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; +import java.net.MalformedURLException; +import org.apache.log4j.Logger; + +import uk.gov.westsussex.ldap.mapping.LDAPMapper; +import uk.gov.westsussex.ldap.mapping.LDAPAttributeMapper; +import uk.gov.westsussex.ldap.mapping.LDAPProfileMapper; + +/** + * Configuration of a particular LDAP directory to be used for Aplaws Authentication + * + * @author Chris Gilbert <chr...@we...> + * @version $Id: LDAPConfig.java,v 1.5 2007/07/31 13:16:44 cgyg9330 Exp $ + */ +public class LDAPConfig extends AbstractConfig { + public final static String versionId = + "$Id: LDAPConfig.java,v 1.5 2007/07/31 13:16:44 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/18 11:10:20 $"; + + private static final Logger s_log = Logger.getLogger(LDAPConfig.class); + + private Parameter searchNode; + private Parameter identifierAttribute; + private Parameter directoryURL; + private Parameter profileMappers; + + + private static Group countyStaffGroup = null; + + public LDAPConfig() { + + searchNode = + new StringParameter( + "uk.gov.westsussex.ldap.ldap_search_node", + Parameter.REQUIRED, + "ou=people,o=wscc,dc=westsussex,dc=gov,dc=uk"); + + identifierAttribute = + new StringParameter( + "uk.gov.westsussex.ldap.ldap_identifier_attribute", + Parameter.REQUIRED, + "uid"); + + directoryURL = + new StringParameter( + "uk.gov.westsussex.ldap.ldap_url", + Parameter.REQUIRED, + "ldap://ldap.server:389/"); + + profileMappers = + new StringArrayParameter( + "uk.gov.westsussex.ldap.profile_mappers", + Parameter.OPTIONAL, + null); + + register(searchNode); + register(identifierAttribute); + register(directoryURL); + register(profileMappers); + + loadInfo(); + + // need to init connection elsewhere + //LDAPConnection.init(getDirectoryURL(), getSearchNode(), getIdentifierAttribute()); + + } + + public String getSearchNode() { + return (String) get(searchNode); + } + public String getIdentifierAttribute() { + return (String) get(identifierAttribute); + } + public String getDirectoryURL() { + return (String) get(directoryURL); + } + + + + public void loadProfileMappers () { + String[] mappers = (String[])get(profileMappers); + for (int i = 0; i < mappers.length; i++) { + try { + LDAPMapper.addMapper((LDAPProfileMapper)Class.forName(mappers[i]).newInstance()); + } catch (InstantiationException e) { + throw new UncheckedWrapperException("Cannot load specified mapper - " + mappers[i], e); + } catch (IllegalAccessException e) { + throw new UncheckedWrapperException("Cannot load specified mapper - " + mappers[i], e); + } catch (ClassNotFoundException e) { + throw new UncheckedWrapperException("Cannot load specified mapper - " + mappers[i], e); + } + } + } + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig_parameter.properties =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig_parameter.properties (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConfig_parameter.properties 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,19 @@ +uk.gov.westsussex.ldap.ldap_search_node.title=LDAP Search Node +uk.gov.westsussex.ldap.ldap_search_node.purpose=Defines the node from which searches are conducted - should be lowest level node that encompasses all required users +uk.gov.westsussex.ldap.ldap_search_node.example=ou=people,o=wscc,dc=westsussex,dc=gov,dc=uk +uk.gov.westsussex.ldap.ldap_search_node.format=[string] + +uk.gov.westsussex.ldap.ldap_identifier_attribute.title=LDAP User Identification attribute +uk.gov.westsussex.ldap.ldap_identifier_attribute.purpose=Defines the key attribute on which users are identified - must have unique values +uk.gov.westsussex.ldap.ldap_identifier_attribute.example=uid +uk.gov.westsussex.ldap.ldap_identifier_attribute.format=[string] + +uk.gov.westsussex.ldap.ldap_url.title=LDAP Directory URL +uk.gov.westsussex.ldap.ldap_url.purpose=Full Address of the directory +uk.gov.westsussex.ldap.ldap_url.example=ldap://ldap.server:389/ +uk.gov.westsussex.ldap.ldap_url.format=[string] + +uk.gov.westsussex.ldap.profile_mappers.title=Profile Mapping Classes +uk.gov.westsussex.ldap.profile_mappers.purpose=Profile Mappers to use +uk.gov.westsussex.ldap.profile_mappers.example=uk.gov.westsussex.mapping.ServiceAreaMapper +uk.gov.westsussex.ldap.profile_mappers.format=[string,string,string] Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConnection.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConnection.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConnection.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,190 @@ +/* + * Created on 30-Sep-03 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.ldap; + +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.LimitExceededException; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.event.EventDirContext; +import javax.naming.event.NamingListener; +import javax.naming.ldap.Control; +import javax.naming.spi.InitialContextFactory; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.security.AccountNotFoundException; + +/** + * @author cgyg9330 + * + * Provides useful methods for accessing the LDAP directory + + */ +public class LDAPConnection { + + private static LDAPConnection singleton = null; + + private static Logger s_log = Logger.getLogger(LDAPConnection.class); + private SearchControls controls = new SearchControls(); + private String searchContext; + private String identifierAttribute; + private String url; + + private LDAPConnection(String url, String searchNode, String identifier) { + controls.setSearchScope(SearchControls.SUBTREE_SCOPE); + searchContext = searchNode; + this.identifierAttribute = identifier; + this.url = url; + } + + public static LDAPConnection getInstance() { + if (singleton == null) { + init( + LDAP.getConfig().getDirectoryURL(), + LDAP.getConfig().getSearchNode(), + LDAP.getConfig().getIdentifierAttribute()); + } + return singleton; + } + + public synchronized static void init( + String url, + String searchNode, + String identifier) { + singleton = new LDAPConnection(url, searchNode, identifier); + + } + + public boolean authenticate(String dn, String password) + throws NamingException { + Properties props = getProps(); + props.setProperty("java.naming.security.principal", dn); + props.setProperty("java.naming.security.credentials", password); + + try { + + DirContext searchContext = new InitialDirContext(props); + searchContext.close(); + return true; + } catch (NamingException e) { + return false; + } + + } + + public void registerListener() { + Properties props = getProps(); + + Context context = null; + try { + + + + + EventDirContext ctx = (EventDirContext) + (new InitialDirContext(props).lookup(searchContext)); + s_log.debug("Event Directory Context retrieved"); + NamingListener listener = LDAPDirectory.retrieve(); + ctx.addNamingListener(searchContext, EventDirContext.SUBTREE_SCOPE, listener); + s_log.debug("naming listener retrieved"); + } catch (NamingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if (context != null) { + try { + context.close(); + } catch (NamingException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + } + } + public LDAPUser getLDAPUser(String ldapID) + throws NamingException, AccountNotFoundException { + Properties props = getProps(); + + + String identifier = identifierAttribute + "=" + ldapID; + + DirContext context = null; + try { + // ignore case of > 1 record + context = new InitialDirContext(props); + NamingEnumeration results = + context.search(searchContext, identifier, controls); + if (!results.hasMoreElements()) { + throw new AccountNotFoundException( + "No user record for " + + identifier + + " in " + + searchContext); + } + SearchResult result = (SearchResult) results.next(); + return new LDAPUser(result); + } finally { + if (context != null) { + context.close(); + } + } + } + + public NamingEnumeration search(String criteria) throws NamingException { + + Properties props = getProps(); + + DirContext context = null; + try { + + context = new InitialDirContext(props); + return context.search(searchContext, criteria, controls); + + } catch (NamingException e) { + throw e; + } finally { + if (context != null) { + context.close(); + } + } + + } + + + public String getSearchContext() { + return searchContext; + } + + /** + * @return + */ + private Properties getProps() { + Properties props = new Properties(); + props.setProperty( + "java.naming.factory.initial", + "com.sun.jndi.ldap.LdapCtxFactory"); + //props.setProperty("com.sun.jndi.ldap.connect.timeout", "1"); + + /*props.setProperty( + "java.naming.factory.initial", + "com.ibm.jndi.LDAPCtxFactory");*/ + + props.setProperty("java.naming.provider.url", url); + return props; + } + + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConstants.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConstants.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPConstants.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,43 @@ +/* + * Created on 15-Dec-05 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.ldap; + +/** + * @author cgyg9330 + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public interface LDAPConstants { + + public static final String FIRST_NAME = "givenName"; + public static final String LAST_NAME = "sn"; + public static final String INTERNAL_PHONE = "telephoneNumber"; + public static final String FULL_PHONE = "internationalisdnnumber"; + public static final String COMMON_NAME = "cn"; + public static final String UID = "uid"; + public static final String EMAIL = "mail"; + public static final String JOB_TITLE = "title"; + public static final String MOBILE = "mobile"; + public static final String PERSONAL_TITLE = "personalTitle"; + public static final String TEAM = "departmentNumber"; + public static final String UNIT = "ou"; + public static final String GROUP = "businessCategory"; + public static final String LOCATION = "location"; + public static final String BUILDING = "physicalDeliveryOfficeName"; + public static final String TOWN = "l"; + public static final String POSTCODE = "postalcode"; + + + + + + + + + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPDirectory.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPDirectory.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPDirectory.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,224 @@ +/* + * Created on 15-Dec-05 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.ldap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchResult; +import javax.naming.event.NamingEvent; +import javax.naming.event.NamingExceptionEvent; +import javax.naming.event.NamingListener; +import javax.naming.event.ObjectChangeListener; +import javax.naming.ldap.UnsolicitedNotificationEvent; +import javax.naming.ldap.UnsolicitedNotificationListener; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.security.AccountNotFoundException; + +/** + * @author cgyg9330 + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public class LDAPDirectory implements LDAPConstants, UnsolicitedNotificationListener { + + private static LDAPDirectory theDirectory = null; + private static Logger s_log = Logger.getLogger(LDAPDirectory.class); + public static LDAPDirectory retrieve() { + if (theDirectory == null) { + theDirectory = new LDAPDirectory(); + } + return theDirectory; + } + private LDAPDirectory() { + + } + + public List getMatches(String firstNamePart, String lastNamePart) + throws NamingException { + Map simpleSearch = new HashMap(); + simpleSearch.put(FIRST_NAME, firstNamePart); + simpleSearch.put(LAST_NAME, lastNamePart); + return getMatches(simpleSearch, null, null); + } + + + + /** + * retrieve matches for entered search criteria. Criteria + * is a map of string attribute name to string values. + * + * Results will be those users with attributes that contain + * any part of the attribute values. + * @param criteria + * @return + * @throws NamingException + */ + public List getMatches (Map criteria) throws NamingException { + return getMatches(criteria, null, null); + } + + /** + * create a search by ANDING together criteria (attribute -> required value) contained in the map + * and also checking for optional searchAll in any of the attributes specified in the last parameter + * + * @param criteria a Map of attribute names to required values. These are ANDED together in the filter. + * values are automatically prepended and postpended with * wildcard, but the values may contain wildcards + * + * @param searchAll an optional string. If not null, the filter will search for the value in any of the + * attributes in the attributes parameter. This enables a free text style search - search for chris, and it + * can return anyone with the value chris in first name, last name, location etc + * + * @param attributes used in conjunction with searchAll param. searchAll string is checked in all the attributes + * specified here and the record returned if searchAll appears in any of the attributes + * + */ + public List getMatches( + Map criteria, + String searchAll, + String[] attributes) + throws NamingException { + String filter = getSearchFilter(criteria, searchAll, attributes); + s_log.debug("filter is " + filter); + + NamingEnumeration results = LDAPConnection.getInstance().search(filter); + List resultList = new ArrayList(); + while (results.hasMore()) { + LDAPUser user = new LDAPUser((SearchResult) results.next()); + resultList.add(user); + + } + Collections.sort(resultList); + + return resultList; + + } + + /** + * retrieve a user based on any unique attribute + * @param identifier + * @param identifyingAttribute + * @return + * @throws AccountNotFoundException if either no user is found, or more than one user is found with the + * specified attribute value + * @throws NamingException + */ + public LDAPUser getUser(String identifier, String identifyingAttribute) throws AccountNotFoundException, NamingException { + if (UID.equals(identifyingAttribute)) { + return getUser(identifier); + } + Map map = new HashMap(); + map.put(identifier, identifyingAttribute); + List matches = getMatches(map); + if (matches.size() == 0) { + throw new AccountNotFoundException("No user found with " + identifier + " of " + identifyingAttribute); + } + if (matches.size() > 1) { + throw new AccountNotFoundException("More than one user found with " + identifier + " of " + identifyingAttribute); + } + return (LDAPUser)matches.get(0); + + } + public LDAPUser getUser(String id) + throws AccountNotFoundException, NamingException { + return LDAPConnection.getInstance().getLDAPUser(id); + } + + + + /* public LDAPUser getUser(String uid) { + + }*/ + + + private String getSearchFilter( + Map criteria, + String searchAll, + String[] attributes) { + + StringBuffer filter = new StringBuffer(); + + // experimentation shows that Error code 87 (invalid filter) occurs when some and or or blocks are too + // long - restricting to 16 characters seems to be safe and with wildcards being automatically added + // shouldn't cause users problems + + // start of filter + filter.append("(&"); + if (!StringUtils.isBlank(searchAll)) { + filter.append("(|"); + for (int i = 0; i < attributes.length; i++) { + + filter.append( + "(" + + attributes[i] + + "=*" + + StringUtils.substring(searchAll, 0, 16) + + "*)"); + } + + filter.append(")"); + } + + Iterator it = criteria.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + String attribute = (String) entry.getKey(); + String value = (String) entry.getValue(); + if (!StringUtils.isBlank(value)) { + // because we use wildcards, blank is just excess search criteria that will return all records, so + // exclude from filter. If we didn't use wildcards, then blank would be more significant - ie + // people without a value in the specified attribute. If this kind of search was required, we would + // need to implement a new getExactMatches method that didn't add wildcards and didn't exclude + // blank criteria from the filter + filter.append( + "(" + + attribute + + "=*" + + StringUtils.substring(value, 0, 16) + + "*)"); + } + } + + + + // end of filter + filter.append("(objectClass=inetOrgPerson)(!(employeeType=previous)))"); + + return filter.toString(); + + } + /* (non-Javadoc) + * @see javax.naming.ldap.UnsolicitedNotificationListener#notificationReceived(javax.naming.ldap.UnsolicitedNotificationEvent) + */ + public void notificationReceived(UnsolicitedNotificationEvent arg0) { + s_log.error("object changed " + arg0.getSource()); + + } + /* (non-Javadoc) + * @see javax.naming.event.NamingListener#namingExceptionThrown(javax.naming.event.NamingExceptionEvent) + */ + public void namingExceptionThrown(NamingExceptionEvent arg0) { + s_log.error("Naming Exception " + arg0.getSource()); + + } + + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPMappingLoginModule.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPMappingLoginModule.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPMappingLoginModule.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,143 @@ +/* + * Created on 30-Sep-03 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.ldap; + +import java.math.BigDecimal; +import java.util.Map; +import java.util.List; +import java.util.Iterator; + +import javax.naming.NamingException; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginException; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.authentication.MultipleDirectoryLoginModule; +import uk.gov.westsussex.authentication.UserTracker; +import uk.gov.westsussex.authentication.Utils; +import uk.gov.westsussex.ldap.mapping.LDAPMapper; + +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.kernel.EmailAddress; +import com.arsdigita.kernel.Group; +import com.arsdigita.kernel.GroupCollection; +import com.arsdigita.kernel.PersonName; +import com.arsdigita.kernel.User; +import com.arsdigita.kernel.UserCollection; +import com.arsdigita.kernel.security.AccountNotFoundException; +import com.arsdigita.kernel.security.MappingLoginModule; + +/** + * @author cgyg9330 + * + * Creates or updates a ccm user based on the LDAP user. + * + * + */ +public class LDAPMappingLoginModule extends MappingLoginModule { + + private static final Logger s_log = + Logger.getLogger(LDAPMappingLoginModule.class); + + private Subject m_subject; + private CallbackHandler m_handler; + private Map m_shared; + private Map m_options; + + // implements LoginModule + public void initialize( + Subject subject, + CallbackHandler handler, + Map shared, + Map options) { + super.initialize(subject, handler, shared, options); + m_subject = subject; + m_handler = handler; + m_shared = shared; + m_options = options; + + } + + protected BigDecimal getUserID(String ldapID) + throws AccountNotFoundException, LoginException { + s_log.debug("START - getUserID"); + User user = getUser(ldapID); + return user.getID(); + } + + private User getUser(String ldapID) + throws AccountNotFoundException, LoginException { + + LDAPUser ldapUser = + (LDAPUser) m_shared.get(LDAPAuthenticationModule.LDAP_USER); + + if (null == ldapUser) { + // could happen - not sure how but Matt (redhat) did think up a possible example + try { + ldapUser = LDAPConnection.getInstance().getLDAPUser(ldapID); + + } catch (NamingException e) { + throw new LoginException("LDAP error occurred"); + } + } + User ccmUser; + UserCollection users = User.retrieveAll(); + users.addEqualsFilter( + "primaryEmail", + ((String) ldapUser.getMail().get(0))); + try { + if (users.next()) { + s_log.debug("retrieving existing ccm user"); + ccmUser = users.getUser(); + + } else { + s_log.debug("creating new ccm user"); + ccmUser = new User(true); + UserTracker.setNew(ccmUser); + } + } finally { + users.close(); + + } + updateCCMUserAttributes(ldapUser, ccmUser); + LDAPMapper.retrieve().initUser(ldapUser, ccmUser); + /*GroupCollection groups = ccmUser.getAllGroups(); + Group countyStaffGroup = LDAPConnection.getConfig().getCountyStaffGroup(); + groups.addEqualsFilter(Group.ID, countyStaffGroup.getID()); + if (groups.size() == 0) { + countyStaffGroup.addMember(ccmUser); + + } + */ + return ccmUser; + + } + + public static void updateCCMUserAttributes(LDAPUser ldapUser, User ccmUser) { + List addresses = ldapUser.getMail(); + Iterator it = addresses.iterator(); + while (it.hasNext()) { + ccmUser.addEmailAddress(new EmailAddress((String) it.next())); + } + + s_log.debug( + addresses.size() + + " addresses retrieved. setting primary address as " + + (String) addresses.get(0)); + ccmUser.setPrimaryEmail(new EmailAddress(((String) addresses.get(0)))); + + PersonName name = ccmUser.getPersonName(); + // added 29/6/04 - new requirement for integration with Jive forums. User names must be unique + // Utils class deals with addition of digits to the end of surnames + // if no need for unique names, then just set givenname and familyname of personname + Utils.setCCMName(name, ldapUser.getGivenName(), ldapUser.getSN()); + ccmUser.save(); + } + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPUser.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPUser.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/LDAPUser.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,319 @@ +/* + * Created on 30-Sep-03 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.ldap; + +import java.util.StringTokenizer; +import java.util.List; +import java.util.ArrayList; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchResult; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +/** + * @author cgyg9330 + * + * Encapsulates a user from the LDAP directory + */ + +public class LDAPUser implements LDAPConstants, Comparable { + private static final Logger s_log = Logger.getLogger(LDAPUser.class); + + private String dn = null; + private String uid = null; + private String cn = null; + private String givenName = null; + private String sn = null; + private String phoneNumber = null; + private List mail = new ArrayList(); + private String jobTitle = null; + private String mobilePhone = null; + private String team = null; + private String unit = null; + private String group = null; + private String location = null; + private String building = null; + private String town = null; + private String personalTitle = null; + private String postcode = null; + + public LDAPUser(SearchResult result) throws NamingException { + Attributes attrs = result.getAttributes(); + + if (s_log.isDebugEnabled()) { + StringBuffer output = new StringBuffer(); + + output.append("Name: ").append(result.getName()).append("\n"); + + NamingEnumeration enum = attrs.getAll(); + while (enum.hasMoreElements()) { + Attribute attr = (Attribute) enum.nextElement(); + + output.append(attr.getID()).append(": "); + output.append(attr.get().toString()).append("\n"); + } + s_log.debug(output); + } + + dn = result.getName(); + s_log.debug("dn = " + dn); + if (result.isRelative()) { + dn += "," + LDAPConnection.getInstance().getSearchContext(); + } + Attribute cnInDirectory = (Attribute) attrs.get(COMMON_NAME); + if (null != cnInDirectory) { + cn = (String) cnInDirectory.get(); + + } + Attribute snInDirectory = (Attribute) attrs.get(LAST_NAME); + if (null != snInDirectory) { + sn = (String) snInDirectory.get(); + + } + Attribute givenNameInDirectory = (Attribute) attrs.get(FIRST_NAME); + if (null != givenNameInDirectory) { + givenName = (String) givenNameInDirectory.get(); + + } else { + // temporary workaround while givenName is not available in LDAP directory + givenName = "n/a"; + } + + Attribute uidInDirectory = (Attribute) attrs.get(UID); + if (null != uidInDirectory) { + uid = (String) uidInDirectory.get(); + + } + + Attribute mobilePhoneInDirectory = (Attribute) attrs.get(MOBILE); + if (null != mobilePhoneInDirectory) { + mobilePhone = (String) mobilePhoneInDirectory.get(); + + } + + Attribute teamInDirectory = (Attribute) attrs.get(TEAM); + if (null != teamInDirectory) { + team = (String) teamInDirectory.get(); + + } + + Attribute unitInDirectory = (Attribute) attrs.get(UNIT); + if (null != unitInDirectory) { + unit = (String) unitInDirectory.get(); + + } + + Attribute groupInDirectory = (Attribute) attrs.get(GROUP); + if (null != groupInDirectory) { + group = (String) groupInDirectory.get(); + + } + + Attribute locationInDirectory = (Attribute) attrs.get(LOCATION); + if (null != locationInDirectory) { + location = (String) locationInDirectory.get(); + + } + + Attribute buildingInDirectory = (Attribute) attrs.get(BUILDING); + if (null != buildingInDirectory) { + building = (String) buildingInDirectory.get(); + + } + + Attribute townInDirectory = (Attribute) attrs.get(TOWN); + if (null != townInDirectory) { + town = (String) townInDirectory.get(); + + } + Attribute postcodeInDirectory = (Attribute) attrs.get(POSTCODE); + if (null != postcodeInDirectory) { + postcode = (String) postcodeInDirectory.get(); + + } + + Attribute personalTitleInDirectory = + (Attribute) attrs.get(PERSONAL_TITLE); + if (null != personalTitleInDirectory) { + personalTitle = (String) personalTitleInDirectory.get(); + + } + + s_log.debug("uid = " + uid); + Attribute phoneInDirectory = (Attribute) attrs.get(FULL_PHONE); + if (null != phoneInDirectory) { + phoneNumber = (String) phoneInDirectory.get(); + + } + Attribute mailInDirectory = (Attribute) attrs.get(EMAIL); + if (mailInDirectory != null) { + + NamingEnumeration enum = mailInDirectory.getAll(); + while (enum.hasMore()) { + mail.add(((String) enum.next()).toLowerCase()); + } + } + if (mail.size() == 0) { + + //generate made up email address as it needs to be stored + // in the ccm user object and needs to be unique + mail.add(uid.toLowerCase() + "@example.com"); + } + + Attribute titleInDirectory = (Attribute) attrs.get(JOB_TITLE); + if (null != titleInDirectory) { + jobTitle = (String) titleInDirectory.get(); + + } + if (s_log.isDebugEnabled()) { + s_log.debug("Fetched user with dn " + dn); + } + } + + + + public String getUID() { + return uid; + } + public String getDN() { + return dn; + } + + public boolean authenticate(String password) throws NamingException { + return LDAPConnection.getInstance().authenticate(dn, password); + } + + public String getCN() { + return cn; + } + + public String getGivenName() { + return givenName; + } + + public String getSN() { + return sn; + } + + public List getMail() { + List mailAddresses = mail; + // deal with situation where user has no email address so + // made up one has been added + if (StringUtils.contains((String)mail.get(0), "@example.com")) { + mail.add(0, "No email address"); + + } + return mail; + } + + public String getPhone() { + return phoneNumber; + } + + public String getJobTitle() { + return jobTitle; + + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object o) { + LDAPUser other = (LDAPUser) o; + + + + + int surnameCompare = + getSN().toLowerCase().compareTo(other.getSN().toLowerCase()); + + if (surnameCompare != 0) { + return surnameCompare; + } + int firstNameCompare = + getGivenName().toLowerCase().compareTo( + other.getGivenName().toLowerCase()); + if (firstNameCompare != 0) { + return firstNameCompare; + } + int emailCompare = + ((String)getMail().get(0)).toLowerCase().compareTo( + ((String)other.getMail().get(0)).toLowerCase()); + if (emailCompare != 0) { + return emailCompare; + } + return getUID().compareTo(other.getUID()); + + + + } + /** + * @return + */ + public String getBuilding() { + return building; + } + + /** + * @return + */ + public String getGroup() { + return group; + } + + /** + * @return + */ + public String getLocation() { + return location; + } + + /** + * @return + */ + public String getMobilePhone() { + return mobilePhone; + } + + /** + * @return + */ + public String getTeam() { + return team; + } + + /** + * @return + */ + public String getTown() { + return town; + } + + public String getPostcode() { + return postcode; + } + + /** + * @return + */ + public String getUnit() { + return unit; + } + + /** + * @return + */ + public String getPersonalTitle() { + return personalTitle; + } + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Loader.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Loader.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/Loader.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public + * License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of + * the License at http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + */ +package uk.gov.westsussex.ldap; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Date; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.Kernel; +import com.arsdigita.kernel.KernelExcursion; +import com.arsdigita.loader.PackageLoader; +import com.arsdigita.london.terms.Domain; +import com.arsdigita.london.terms.Term; +import com.arsdigita.london.terms.Util; +import com.arsdigita.runtime.ScriptContext; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.web.Application; +import com.arsdigita.web.ApplicationType; + +/** + * Loader. + * + * @author Justin Ross <jr...@re...> + * @version $Id: Loader.java,v 1.2 2007/07/31 13:16:44 cgyg9330 Exp $ + */ +public class Loader extends PackageLoader { + public final static String versionId = + "$Id: Loader.java,v 1.2 2007/07/31 13:16:44 cgyg9330 Exp $" + + "$Author: cgyg9330 $" + + "$DateTime: 2004/03/17 08:31:10 $"; + + private static final Logger s_log = Logger.getLogger(Loader.class); + + public void run(final ScriptContext ctx) { + new KernelExcursion() { + public void excurse() { + setEffectiveParty(Kernel.getSystemParty()); + setupApplication(); + + } + } + .run(); + } + + private void setupApplication() { + ApplicationType type = + new ApplicationType("LDAP", LDAP.BASE_DATA_OBJECT_TYPE); + + s_log.debug( + "app type created - now lets create an application instance"); + LDAP app = + (LDAP) Application.createApplication( + type, + "ldap", + "LDAP", + null, + true); + + } + + + +} + + Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TempIDTracker.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TempIDTracker.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TempIDTracker.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,107 @@ +package uk.gov.westsussex.ldap; + +import org.apache.log4j.Logger; + +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.domain.DomainObject; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.persistence.OID; + +public class TempIDTracker extends DomainObject { + private static Logger s_log = Logger.getLogger(TempIDTracker.class); + + // pdl attribute names + + public static final String UID = "uid"; + + public static final String MAIL = "email"; + + public static final String FIRST_NAME = "firstName"; + + public static final String LAST_NAME = "lastName"; + + public static final String APPLICATION = "application"; + + public static final String BASE_DATA_OBJECT_TYPE = "uk.gov.westsussex.LDAPTempIDTracker"; + + public static final String LOCALITIES = "localities"; + public static final String SCHOOL_VISITS = "school visits"; + public static final String CALENDAR = "calendar"; + // standard constructors + + public TempIDTracker() { + super(BASE_DATA_OBJECT_TYPE); + } + + public TempIDTracker(String uid, String mail, String firstName, String lastName, String application) { + this(); + setUID(uid); + setMail(mail); + setFirstName(firstName); + setLastName(lastName); + setApplication(application); + + } + + public static TempIDTracker retrieve (String uid) { + return new TempIDTracker(new OID(BASE_DATA_OBJECT_TYPE, uid)); + } + + public TempIDTracker(String objectType) throws DataObjectNotFoundException { + super(objectType); + } + + public TempIDTracker(DataObject obj) { + super(obj); + } + + + + public TempIDTracker(OID oid) { + super(oid); + } + + + //////////////////////// + ////// + // Getters + ////// + /////////////////////// + + + public String getUID() { + return (String)get(UID); + } + + + + ////////////////////// + ///// + // Setters and Mutators + ///// + //////////////////// + + public void setUID(String uid) { + set(UID, uid); + } + + public void setMail(String mail) { + set(MAIL, mail); + + } + public void setFirstName(String firstName) { + set(FIRST_NAME, firstName); + + } + public void setLastName(String lastName) { + set(LAST_NAME, lastName); + + } + public void setApplication(String application) { + set(APPLICATION, application); + + } + + + +} Added: aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TrackTempIDScheduler.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TrackTempIDScheduler.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-ldap/src/uk/gov/westsussex/ldap/TrackTempIDScheduler.java 2007-09-17 09:00:42 UTC (rev 1633) @@ -0,0 +1,254 @@ +/* + * 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 uk.gov.westsussex.ldap; + +import java.util.Timer; + +import org.apache.commons.cli.CommandLine; +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.Kernel; +import com.arsdigita.kernel.KernelExcursion; +import com.arsdigita.kernel.security.AccountNotFoundException; +import com.arsdigita.packaging.Program; +import com.arsdigita.persistence.DataQuery; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.persistence.TransactionContext; +import com.arsdigita.util.UncheckedWrapperException; + +/** + * Update temporary id -> user details records for all applications that store + * an LDAP ID. This should be carried out separately by each application, but as + * it is temporary (until County directory can notify apps of changes) I have + * dumped it in here + * + * @author Chris Gilbert(chris.gilbert@... [truncated message content] |
From: <chr...@fe...> - 2007-09-17 09:00:34
|
Author: chrisg23 Date: 2007-09-17 11:00:21 +0200 (Mon, 17 Sep 2007) New Revision: 1630 Added: aplaws/trunk/ccm-core/web/assets/fckeditor/config/fckconfig_forum.js Log: FCK Config for forum application - disables browsing of server file system or aplaws content centre when addign a link as this may be used by public - to use this, need to specify com.arsdigita.forum.use_wysiwyg_editor=true. If HTML Area is set as your editor, then browsing is already disabled for forum in the foorum code. If you have set FCK as your editor then this config will be used. Added: aplaws/trunk/ccm-core/web/assets/fckeditor/config/fckconfig_forum.js =================================================================== --- aplaws/trunk/ccm-core/web/assets/fckeditor/config/fckconfig_forum.js (rev 0) +++ aplaws/trunk/ccm-core/web/assets/fckeditor/config/fckconfig_forum.js 2007-09-17 09:00:21 UTC (rev 1630) @@ -0,0 +1,32 @@ +/* + * FCKeditor - The text editor for internet + * Copyright (C) 2003-2005 Frederico Caldeira Knabben + * + * Custom amendments for Forum application. + */ +FCKConfig.FillEmptyBlocks = false ; + +FCKConfig.ToolbarSets["Basic"] = [ + ['Source','-','Preview','-'], + ['Bold','Italic','Underline','StrikeThrough','-','Subscript','Superscript'], + ['Cut','Copy','Paste','PasteText','PasteWord','-','Print'], + '/', + ['OrderedList','UnorderedList','-','Outdent','Indent'], + ['Link','Unlink'], + ['Undo','Redo','-','SelectAll'], + ['TextColor','BGColor'], + ['Rule','SpecialChar','Smiley'], + '/', + ['FontFormat','FontName','FontSize'] +] ; + +//only licenced for private use +//FCKConfig.SpellChecker = 'ieSpell' ; // 'ieSpell' | 'SpellerPages' +//FCKConfig.IeSpellDownloadUrl = 'http://www.iespell.com/rel/ieSpellSetup211325.exe' ; + +FCKConfig.LinkUpload = false ; +FCKConfig.LinkBrowser = false ; // forum - don't allow users to browse content centre or server file system +FCKConfig.ImageBrowser = false ; +FCKConfig.FlashBrowser = false ; + +FCKConfig.SmileyImages = ['regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','confused_smile.gif','tounge_smile.gif','embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angry_smile.gif','angel_smile.gif','shades_smile.gif','devil_smile.gif','cry_smile.gif','lightbulb.gif','thumbs_down.gif','thumbs_up.gif','heart.gif','broken_heart.gif','kiss.gif','envelope.gif'] ; |
From: <chr...@fe...> - 2007-09-17 09:00:33
|
Author: chrisg23 Date: 2007-09-17 11:00:20 +0200 (Mon, 17 Sep 2007) New Revision: 1629 Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/ aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.css aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.xsl aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/ aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/ aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/archived-16x16.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/back-to-16x16.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/delete-16x16.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/edit-16x16.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/info-16x16.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/post-16x16.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/read.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/reply-16x16.gif aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/watch-16x16.gif Log: Example theme files for updated version of forum application Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.css =================================================================== --- aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.css (rev 0) +++ aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.css 2007-09-17 09:00:20 UTC (rev 1629) @@ -0,0 +1,402 @@ +@import url("css/main.css"); +<--@import url("/css/acs-master.css");--> +<--@import url("/css/tabbed-pane/tabbed-pane.css");--> + + +body { + margin: 0; + background-color: #FFF; + color: #000; + font-family: Verdana, Arial, Helvetica, sans-serif; +} + +.forum { + font-size: 0.8em; + margin: 8px 0 10px 2px; +} + +.content { + margin-left: 10px; +} + + +/*form submission*/ + +.forumSelect { + margin-top: 0.2em; + float:left; +} +.forumButton input { + float:left; + margin-top: 0.2em; + margin-right: 5px; + padding-left: 2px; + padding-right: 2px; + color: #000; + background-color: #CCC; + vertical-align: middle; + border: 1px solid #000; + +} + +.submit { + clear: both; + margin-left: 100px; +} + +/* +view all threads + +*/ + +#selectLabel { + float:left; + color: white; + padding-top: 0.2em; + padding-left: 0.2em; +} +#selectCategory { + background-color: #999; + height: 2.2em; + +} + +#threadHead { + background-color: #E5F3FC; + font-weight: bold; + height: 1.5em; + padding-left: 0.2em; + margin-bottom: 0.3em; + +} +#noMessages { + clear:both; + float:left; + width: 100%; + +} +.subject { + clear: both; + float:left; + width: 190px; + padding: 4px 0 4px 22px; + background-position: 4px 0.7em; + background-repeat: no-repeat; + background-image: url("images/forum/read.gif"); +} + +.topic { + clear:both; + float:left; + width: 212px; + padding: 4px 0 4px 0px; +} + +.threadSubject { + clear:both; + float:left; + width: 140px; + padding: 4px 0 4px 0px; +} + +.author { + float:left; + width: 100px; + padding: 4px 0 4px 0px; + text-align: center; +} + +.replies { + float:left; + text-align: center; + width: 60px; + padding: 4px 0 4px 0px; +} + +.threads { + float:left; + text-align: center; + width: 75px; + padding: 4px 0 4px 0px; +} + +.status { + float:left; + width: 75px; + padding: 4px 0 4px 0px; +} + +.delete { + float:left; + text-align: center; + width: 50px; + padding: 4px 0 4px 0px; +} + +.lastPost { + float: left; + text-align: center; + width: 150px; + padding: 4px 0 4px 0px; +} + +#newTopicLink a { + padding: 4px 0 4px 22px; + background-position: 0px 0.3em; + background-repeat: no-repeat; + background-image: url("images/forum/post-16x16.gif"); +} + +#viewAllTopicsLink a { + float: left; + padding: 4px 15px 4px 22px; + background-position: 0px 0.3em; + background-repeat: no-repeat; + background-image: url("images/forum/back-to-16x16.gif"); + +} + +#watchTopicLink a { + float: left; + padding: 4px 0 4px 22px; + background-position: 0px 0.3em; + background-repeat: no-repeat; + background-image: url("images/forum/watch-16x16.gif"); + margin-left: 0.8em; + margin-bottom: 0.8em; +} + +#info { + clear:both; + padding: 4px 0 4px 22px; + background-position: 0px 0.6em; + background-repeat: no-repeat; + background-image: url("images/forum/info-16x16.gif"); + color: green; + margin-left: 0.8em; +} + +.post { + clear: both; + margin-bottom: 0.8em; + font-size: 1em; +} + +.postOdd { + background-color: #EFEFEF; + +} + +.postEven { + background-color: #FFFFFF; +} + +#topic { + margin-bottom: 0.8em; + font-weight: bold; +} + +.actions { + float: right; + margin-right: 0.3em; +} + +.header { + float: left; + margin-left: 0.3em; + font-weight: bold; +} + +.message { + clear: both; + float: left; + width:98%; + margin-left: 0.3em; + margin-bottom: 0.5em; +} + +.postDetails { + clear: both; + float: left; + margin-bottom: 0.5em; + margin-left: 0.3em; +} + +.bar { + clear:both; + float:left; + width: 98%; + padding-left: 0.3em; + padding-right: 0.3em; + padding-top: 0.5em; + padding-bottom: 0.5em; +} + +.replyBar { + clear:both; + padding-right: 0.3em; + padding-top: 0.5em; + padding-bottom: 0.5em; +} + + +.clearGroup { + clear:both; + margin-bottom: 0.5em; +} + +.columnOne { + float: left; + width: 100px; + font-size: 1em; +} + +.columnOneWide { + float: left; + width: 130px; + font-size: 1em; +} + +.columnTwo { + display: inline; + font-size: 1em; + margin: 0; +} + + +.columnTwo input { + width: 300px; +} + +.columnTwo select { + width: 300px; +} + + +.columnTwo textArea { + width: 300px; +} + +.columnThree { + font-size: 1em; +} + +#title { + float:left; + width: 100%; + + font-size: 1.2em; +} + + +fieldset { + padding: 0.3em 0.3em 0.3em 0.3em; +} + +.fileLink { + margin-left: 0.5em; + clear: both; + float:left; +} + + +textarea { + width: 450px; +} + +.validationError { + color: red; + margin-left: 100px; +} + +#images { + clear: both; + float:left; + margin-left: 0.3em; +} + +#plainText { + clear: both; + font-size: 1em; + margin-bottom: 0.5em; +} + +li { + list-style-position: inside; +} + +.expiry input { + width: 50px; +} + +.fileTable { + width: 100%; +} + + +pre { + + font-size: 1em; +} + + +/* Contents of tabbed-pane.css - forum tabs */ + +div.tabbed-pane div, div.tabbed-pane table.tab-set, div.tabbed-pane table.tab-set td { + margin: 0; + border: 0; + padding: 0; +} + +div.tabbed-pane table { + border-collapse: collapse; +} + +div.tabbed-pane table.tab-set { + margin-left: 10px; +} + +div.tabbed-pane table.tab-set a { + text-decoration: none; + color: rgb(63,63,63); +} + +div.tabbed-pane table.tab-set td.tab-label { + margin-left: 4px; + padding: 5px 0 3px 10px; + + background: rgb(225,225,225) url(/css/tabbed-pane/tab-unselected.png) no-repeat; +} + +div.tabbed-pane table.tab-set td.tab-end { + width: 10px; + background: url(/css/tabbed-pane/tab-unselected-end.png) no-repeat; +} + +div.tabbed-pane table.tab-set td.current-tab-label { + margin-left: 4px; + padding: 5px 0 3px 10px; + background: rgb(162,30,30) url(/css/tabbed-pane/tab-selected.png) no-repeat; + color: white; +} + +div.tabbed-pane table.tab-set td.current-tab-end { + width: 10px; + background: url(/css/tabbed-pane/tab-selected-end.png) no-repeat; +} + +div.tabbed-pane table.tab-set td.tab-spacer { + width: 4px; +} + +div.tabbed-pane table.rule { + margin-left: 10px; + width: 100%; + background: rgb(162,30,30) url(/css/tabbed-pane/tab-bar.png) repeat-x; + height: 10px; +} + +div.tabbed-pane div.current-pane { + width: 100%; +} + Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.xsl =================================================================== --- aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.xsl (rev 0) +++ aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/forum-index.xsl 2007-09-17 09:00:20 UTC (rev 1629) @@ -0,0 +1,1294 @@ +<xsl:stylesheet xmlns:forum="http://www.arsdigita.com/forum/1.0" + xmlns:bebop="http://www.arsdigita.com/bebop/1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:nav="http://ccm.redhat.com/london/navigation" + xmlns:search="http://rhea.redhat.com/search/1.0" + xmlns:portal="http://www.uk.arsdigita.com/portal/1.0" + xmlns:cms="http://www.arsdigita.com/cms/1.0" + exclude-result-prefixes="xsl bebop nav search portal forum cms" + version="1.0"> + + <xsl:import href="../../../../ROOT/packages/bebop/xsl/bebop.xsl" /> + <xsl:import href="../../../../ROOT/packages/ui/xsl/ui.xsl" /> + <xsl:import href="../../../../ROOT/packages/bebop/xsl/bebop.xsl" /> + <xsl:import href="lib/header.xsl" /> + <xsl:import href="lib/lib.xsl" /> + <xsl:import href="lib/leftNav.xsl" /> + <xsl:import href="../../../../ROOT/packages/bebop/xsl/dcp.xsl"/> + + + + + <xsl:param name="theme-prefix" /> + <xsl:param name="context-prefix" /> + <xsl:param name="dispatcher-prefix" /> + + <xsl:output method="html" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" doctype-system="http://www.w3.org/TR/html4/loose.dtd" indent="yes" /> + +<!-- ********************* MAIN PAGE LAYOUT TEMPLATES ********************* --> + + <xsl:template match="bebop:page[@class='simplePage']"> + <html lang="en"> + <head> + <title> + Forum + </title> + <link rel="stylesheet" href="{$theme-prefix}/forum-index.css" type="text/css" media="screen" /> + <link rel="stylesheet" href="{$theme-prefix}/css/print.css" type="text/css" media="print" /> + <script type="text/javascript" src="{$theme-prefix}/css/date.js"></script> + <xsl:call-template name="bebop:dcpJavascript"/> + </head> + + <body> + <a name="Ptop"></a> + <a class="navHide" href="#startcontent" title="Go directly to main content" accesskey="S">Skip over navigation</a> + <span class="hide">|</span> + <xsl:call-template name="header" /> + <xsl:call-template name="mainContent" /> + + <xsl:call-template name="footer" /> + + </body> + </html> + </xsl:template> + + + <xsl:template name="mainContent"> + + <table id="mainLayout" border="0" cellspacing="0" cellpadding="0" summary="navigation and content"> + <tr> + <td> + <img class="lSpacer" src="{$theme-prefix}/images/spacer.gif" alt="" /> + </td> + <td> + <img class="rSpacer" src="{$theme-prefix}/images/spacer.gif" alt="" /> + </td> + </tr> + <tr> + <td id="leftNav" align="left" valign="top"> + <!--LHS NAVIGATION --> + + <xsl:call-template name="leftNav" /> + + </td> + + <td valign="top"> + <span class="hide">|</span> + + <!--CONTENT --> + <a id="startcontent" class="navHide" title="Start of content"></a> + <span class="hide">|</span> + <!--<xsl:call-template name="pageContent" />--> + <div class="pageTitle"> + <h1><xsl:value-of select="/bebop:page/forum:name"/></h1> + </div> + <div class="forum"> + <xsl:apply-templates select="//forum:forum | //forum:threadDisplay | /bebop:page/bebop:form " /> + </div> + </td> + </tr> + </table> + </xsl:template> + +<!-- ******************** POST TEMPLATES ******************************* --> + + <xsl:template name="displayPost"> + <xsl:param name="preview" /> + + <xsl:variable name="class"> + <xsl:choose> + <xsl:when test="position() mod 2"> + <xsl:text>postOdd</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>postEven</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + + <xsl:variable name="indent"> + <xsl:choose> + <xsl:when test="$preview='true'"> + <xsl:value-of select="'0'" /> + </xsl:when> + <xsl:otherwise> + <xsl:choose> + <xsl:when test="count(sortKey) = 0"> + <xsl:value-of select="'0'" /> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="string-length(sortKey) div 3" /> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + + <xsl:if test="count(sortKey) = 0 and $preview='false'"> + <div id="topic" style="margin-left: {$indent}em;">Thread: <xsl:value-of select="subject" /></div> + + <xsl:apply-templates select="../../forum:threadOptions" /> + </xsl:if> + + <div class="post" style="margin-left: {$indent}em;"> + <fieldset class="{$class}"> + <div class="clearGroup"> + <div class="header"> + <xsl:value-of select="subject" /> + <xsl:if test="status != 'approved' and $preview = 'false'"> + ( + <xsl:value-of select="status" /> + ) + </xsl:if> + </div> + + <xsl:if test="$preview = 'false'"> + <div class="actions"> + <xsl:if test="@approveURL"> + <a href="{@approveURL}"> + <img alt="Approve" border="0" src="{$theme-prefix}/images/forum/reward-16x16.gif" /> + </a> + <!--<xsl:text> </xsl:text> + <a href="{@approveURL}">Approve</a>--> + <xsl:text> </xsl:text> + + </xsl:if> + <xsl:if test="@rejectURL"> + <a href="{@rejectURL}" > + <img alt="Reject" border="0" src="{$theme-prefix}/images/forum/delete-16x16.gif" /> + </a> + <!--<xsl:text> </xsl:text> + <a href="{@rejectURL}">Reject</a>--> + <xsl:text> </xsl:text> + </xsl:if> + <xsl:if test="@editURL"> + <a href="{@editURL}"> + <img alt="Edit" border="0" src="{$theme-prefix}/images/forum/edit-16x16.gif" /> + </a> + <!--<xsl:text> </xsl:text> + <a href="{@editURL}">Edit</a>--> + <xsl:text> </xsl:text> + </xsl:if> + <xsl:if test="@deleteURL"> + <a href="{@deleteURL}"> + <img alt="Delete" border="0" src="{$theme-prefix}/images/forum/archived-16x16.gif" /> + </a> + <!--<xsl:text> </xsl:text> + <a href="{@deleteURL}">Delete</a>--> + <xsl:text> </xsl:text> + </xsl:if> + <xsl:if test="@replyURL"> + <xsl:text> </xsl:text> + <xsl:text> </xsl:text> + <xsl:text> </xsl:text> + <a href="{@replyURL}"> + <img alt="Reply" border="0" src="{$theme-prefix}/images/forum/reply-16x16.gif" /> + </a> + <xsl:text> </xsl:text> + <a href="{@replyURL}">Reply</a> + <xsl:text> </xsl:text> + </xsl:if> + </div> + </xsl:if> + </div> + + <div class="postDetails"> + <xsl:text>Posted: </xsl:text> + <xsl:value-of select="sent" /> + <xsl:text> by </xsl:text> + <a href="mailto:{sender/primaryEmail}"> + <xsl:value-of select="sender/displayName" /> + </a> + </div> + + <div class="clearGroup"> + <xsl:for-each select="files"> + <xsl:sort select="fileOrder"/> + <div class="fileLink"> + <xsl:variable name="encodedFileName"> + <xsl:call-template name="url-encode"> + <xsl:with-param name="str" select="./name"/> + </xsl:call-template> + </xsl:variable> + <a href="{$dispatcher-prefix}/cms-service/stream/asset/?asset_id={./id}&file=/{$encodedFileName}"> + <!-- show file in new window for preview to avoid losing the form --> + <xsl:if test="$preview='true'"> + <xsl:attribute name="target">_blank</xsl:attribute> + </xsl:if> + + <xsl:value-of select="name"/></a> + </div> + <div class="fileDescription"> + + </div> + </xsl:for-each> + </div> + + <div class="bar"> + <hr/> + </div> + + <div class="message"> + <xsl:value-of select="body" disable-output-escaping="yes" /> + </div> + + <div id="images"> + <xsl:for-each select="images"> + <xsl:sort select="link/imageOrder"/> + <div class="clearGroup"> + <img src="{$dispatcher-prefix}/cms-service/stream/image/?image_id={./id}" alt="{./description}"/> + </div> + </xsl:for-each> + </div> + </fieldset> + </div> + <table class="data"> + <tfoot> + <tr> + <th colspan="5"> + <xsl:apply-templates select="forum:paginator" mode="page-links" /> + </th> + </tr> + </tfoot> + </table> + </xsl:template> + + <xsl:template match="forum:paginator" mode="page-links"> + <xsl:if test="@pageCount > 1"> + <div class="clearGroup"> + <br/> + <xsl:if test="@pageNumber > 1"> + <a> + <xsl:attribute name="href"> + <xsl:call-template name="make-url"> + <xsl:with-param name="base-url" select="@baseURL" /> + <xsl:with-param name="name" select="@param" /> + <xsl:with-param name="value" select="@pageNumber - 1" /> + </xsl:call-template> + </xsl:attribute> + <xsl:text><<< Previous</xsl:text> + </a> + <xsl:text> </xsl:text> + <xsl:text> </xsl:text> + </xsl:if> + <xsl:text>Page </xsl:text> + <xsl:value-of select="@pageNumber" /><xsl:text> </xsl:text> + <xsl:text>of </xsl:text> + <xsl:value-of select="@pageCount" /><xsl:text> </xsl:text> + <xsl:if test="@pageNumber < @pageCount"> + <xsl:text> </xsl:text> + <xsl:text> </xsl:text> + <a> + <xsl:attribute name="href"> + <xsl:call-template name="make-url"> + <xsl:with-param name="base-url" select="@baseURL" /> + <xsl:with-param name="name" select="@param" /> + <xsl:with-param name="value" select="@pageNumber + 1" /> + </xsl:call-template> + </xsl:attribute> + <xsl:text>Next >>></xsl:text> + </a> + </xsl:if> + </div> + </xsl:if> + </xsl:template> + + <xsl:template match="forum:topicSelector"> + <xsl:if test="count(forum:topic) > 0"> + <div class="content"> + <form action="{@baseURL}" method="get"> + + <div id="selectCategory"> + <div id="selectLabel">Filter by topic:</div> + <select name="{@param}" class="forumSelect"> + <option value="{@anyTopicID}"> + <xsl:if test="@anyTopicID = @currentTopicID"> + <xsl:attribute name="selected"> + <xsl:text>selected</xsl:text> + </xsl:attribute> + </xsl:if> + <xsl:text>All topics</xsl:text> + </option> + <xsl:for-each select="forum:topic"> + <option value="{id}"> + <xsl:if test="id = ../@currentTopicID"> + <xsl:attribute name="selected"> + <xsl:text>selected</xsl:text> + </xsl:attribute> + </xsl:if> + <xsl:value-of select="name" /> + </option> + </xsl:for-each> + <option value="{@noTopicID}"> + <xsl:if test="@noTopicID = @currentTopicID"> + <xsl:attribute name="selected"> + <xsl:text>selected</xsl:text> + </xsl:attribute> + </xsl:if> + <xsl:text>No topic</xsl:text> + </option> + </select> + <div class="forumButton"> + <input type="submit" value="Apply" /> + </div> + </div> + </form> + </div> + </xsl:if> + </xsl:template> + + <xsl:template match="forum:threadList"> + <div class="content"> + <div id="threadHead"> + <div class="topic">Thread</div> + <div class="author">Author</div> + <div class="replies">Replies</div> + <div class="lastPost">Last Post</div> + </div> + <xsl:if test="count(forum:thread) = 0"> + <div id="noMessages">No messages have been posted yet</div> + </xsl:if> + <xsl:for-each select="forum:thread"> + + <div> + <xsl:variable name="class"> + <xsl:choose> + <xsl:when test="position() mod 2"> + <xsl:text>odd</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>even</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + + + <div class="subject"> + <a href="{@url}"> + <xsl:value-of select="root/subject" /> + </a> + </div> + <div class="author"> + <xsl:value-of select="author/displayName" /> + </div> + <div class="replies"> + <xsl:value-of select="numReplies" /> + </div> + <div class="lastPost"> + <xsl:value-of select="lastUpdate" /> + </div> + + </div> + </xsl:for-each> + </div> + + <xsl:apply-templates select="forum:paginator" mode="page-links" /> + + </xsl:template> + + <xsl:template match="forum:threadAlertList"> + <div class="clearGroup"> + <div id="threadHead"> + <div class="threadSubject">Subject</div> + <div class="replies">Replies</div> + <div class="author">Author</div> + <div class="lastPost">Last Post</div> + <div class="status">Status</div> + <div class="delete">Delete</div> + </div> + </div> + <xsl:if test="count(forum:threadAlert) = 0"> + <div> + <em>You are not subscribed to any threads</em> + </div> + </xsl:if> + <xsl:for-each select="forum:threadAlert"> + <xsl:variable name="class"> + <xsl:choose> + <xsl:when test="position() mod 2"> + <xsl:text>postOdd</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>postEven</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <div class="{$class}"> + <div class="threadSubject"> + <a href="{@url}"><xsl:value-of select="thread/root/subject" /></a> + </div> + <div class="replies"> + <xsl:value-of select="thread/numReplies" /> + </div> + <div class="author"> + <xsl:value-of select="thread/author/displayName" /> + </div> + <div class="lastPost"> + <xsl:value-of select="thread/lastUpdate" /> + </div> + <div class="status"> + <xsl:value-of select="thread/root/status" /> + </div> + <div class="delete"> + <input type="checkbox" name="{@param}" value="{id}" /> + </div> + </div> + </xsl:for-each> + </xsl:template> + + <xsl:template match="forum:threadDisplay"> + <div class="content"> + <xsl:for-each select="forum:message"> + <xsl:call-template name="displayPost"> + <xsl:with-param name="preview"> + <xsl:text>false</xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:for-each> + </div> + </xsl:template> + + + + <xsl:template match="forum:topicList"> + <div class="content"> + <div id="threadHead"> + <div class="threadSubject">Topic</div> + <div class="threads">Threads</div> + <div class="lastPost">Last Post</div> + </div> + + <xsl:for-each select="forum:topicSummary"> + <xsl:variable name="class"> + <xsl:choose> + <xsl:when test="position() mod 2"> + <xsl:text>postOdd</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>postEven</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <div class="{$class}"> + <div class="threadSubject"> + <xsl:choose> + <xsl:when test="numThreads > 0"> + <a> + <xsl:attribute name="href"> + <xsl:call-template name="make-url"> + <xsl:with-param name="base-url" select="../@baseURL" /> + <xsl:with-param name="name" select="../@param" /> + <xsl:with-param name="value" select="id" /> + </xsl:call-template> + </xsl:attribute> + <xsl:value-of select="name" /> + </a> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="name" /> + </xsl:otherwise> + </xsl:choose> + </div> + <div class="threads"> + <xsl:value-of select="numThreads" /> + </div> + <div class="lastPost"> + <xsl:choose> + <xsl:when test="numThreads > 0"> + <xsl:value-of select="latestPost" /> + </xsl:when> + <xsl:otherwise> + <em><xsl:text>n/a</xsl:text></em> + </xsl:otherwise> + </xsl:choose> + </div> + </div> + </xsl:for-each> + <xsl:for-each select="forum:noTopicSummary"> + <xsl:variable name="class"> + <xsl:choose> + <xsl:when test="count(../forumTopicSummary) mod 2"> + <xsl:text>postOdd</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>postEven</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <div class="{$class}"> + <div class="threadSubject"> + <xsl:choose> + <xsl:when test="numThreads > 0"> + <a> + <xsl:attribute name="href"> + <xsl:call-template name="make-url"> + <xsl:with-param name="base-url" select="../@baseURL" /> + <xsl:with-param name="name" select="../@param" /> + <xsl:with-param name="value" select="id" /> + </xsl:call-template> + </xsl:attribute> + <em><xsl:text>None</xsl:text></em> + </a> + </xsl:when> + <xsl:otherwise> + <em><xsl:text>None</xsl:text></em> + </xsl:otherwise> + </xsl:choose> + </div> + <div class="threads"> + <xsl:value-of select="numThreads" /> + </div> + <div class="lastPost"> + <xsl:choose> + <xsl:when test="numThreads > 0"> + <xsl:value-of select="latestPost" /> + </xsl:when> + <xsl:otherwise> + <em><xsl:text>n/a</xsl:text></em> + </xsl:otherwise> + </xsl:choose> + </div> + </div> + </xsl:for-each> + </div> + </xsl:template> + + <xsl:template match="forum:forumAlerts"> + <div class="content"> + <h5>Forum Alerts</h5> + <br/> + <xsl:apply-templates select="*" /> + </div> + </xsl:template> + +<!-- <xsl:template match="bebop:form[@name='instantAlerts' or @name='dailyAlerts']//bebop:panelRow"> + <xsl:for-each select="bebop:cell[not(bebop:formWidget)]"> + <xsl:apply-templates /> + </xsl:for-each> + <div class="forumButton"> + <xsl:apply-templates select="bebop:cell/bebop:formWidget[@name='Save' or @name='forum.ui.delete']" /> + </div> + </xsl:template>--> + + <xsl:template match="forum:threadAlerts"> + <div class="content"> + <h5>Thread Alerts</h5> + <br/> + <xsl:apply-templates select="*" /> + </div> + </xsl:template> + + <xsl:template match="forum:forum"> + <xsl:if test="not(descendant::bebop:form/@name = 'newPostForm')"> + <div class="tabbed-pane"> + <table class="tab-set"> + <tr> + <xsl:apply-templates select="forum:forumMode" /> + </tr> + </table> + <table class="rule"> + <tr> + <td></td> + </tr> + </table> + </div> + <div> + <xsl:text> </xsl:text> + </div> + </xsl:if> + <div class="clearGroup"> + <div class="content"> + <xsl:if test="forum:forumMode[@mode = 'threads' and @selected='1'] and not(bebop:form/@name='newPostForm')"> + <xsl:call-template name="br-replace"> + <xsl:with-param name="text" select="/bebop:page/forum:introduction"/> + + </xsl:call-template> + </xsl:if> + </div> + </div> + <xsl:apply-templates select="*[not(name() = 'forum:forumMode')]" /> + </xsl:template> + + <xsl:template match="forum:forumMode"> + <xsl:variable name="title"> + <xsl:choose> + <xsl:when test="@mode = 'threads'"> + <xsl:value-of select="'Threads'" /> + </xsl:when> + <xsl:when test="@mode = 'topics'"> + <xsl:value-of select="'Topics'" /> + </xsl:when> + <xsl:when test="@mode = 'alerts'"> + <xsl:value-of select="'Alerts'" /> + </xsl:when> + <xsl:when test="@mode = 'moderation'"> + <xsl:value-of select="'Moderation'" /> + </xsl:when> + <xsl:when test="@mode = 'permissions'"> + <xsl:value-of select="'Permissions'" /> + </xsl:when> + <xsl:when test="@mode = 'setup'"> + <xsl:value-of select="'Setup'"/> + </xsl:when> + <xsl:when test="@mode = 'categories'"> + <xsl:value-of select="'Categories'"/> + </xsl:when> + </xsl:choose> + </xsl:variable> + <xsl:choose> + <xsl:when test="@selected = 1"> + <td class="current-tab-label"> + <xsl:value-of select="$title" /> + </td> + <td class="current-tab-end" /> + </xsl:when> + <xsl:otherwise> + <td class="tab-label"> + <a href="{@url}"> + <xsl:value-of select="$title" /> + </a> + </td> + <td class="tab-end" /> + </xsl:otherwise> + </xsl:choose> + <td class="tab-spacer" /> + </xsl:template> + + <xsl:template match="forum:memberList"> + <xsl:comment>forum member list</xsl:comment> + <div class="content"> + <fieldset> + <legend><xsl:value-of select="@group" /></legend> + <xsl:apply-templates /> + <xsl:apply-templates select="following-sibling::bebop:form[1]" mode="show"/> + </fieldset> + <br/> + </div> + </xsl:template> + + + <xsl:template match="forum:forumOptions"> + <div class="content"> + <xsl:apply-templates select="*" /> + </div> + <div> + <xsl:text> </xsl:text> + </div> + </xsl:template> + + <xsl:template match="forum:topicOptions"> + <div class="content"> + <xsl:apply-templates select="*" /> + </div> + <div> + <xsl:text> </xsl:text> + </div> + </xsl:template> + + <xsl:template name="make-url"> + <xsl:param name="base-url" /> + <xsl:param name="name" /> + <xsl:param name="value" /> + + <xsl:choose> + <xsl:when test="contains($base-url, '?')"> + <xsl:value-of select="concat($base-url, '&', $name, '=', $value)" /> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="concat($base-url, '?', $name, '=', $value)" /> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + +<!-- ********************** FORM TEMPLATES ********************** --> + + + <xsl:template name="ForumPostForm"> + <xsl:param name="title" select="'Post message'" /> + + <!--<form action="{@action}" name="{@name}" method="get">--> + <form action="{@action}" name="{@name}" enctype="{@enctype}"> + <xsl:attribute name="method"> + <xsl:choose> + <xsl:when test="string-length(@method)=0">post</xsl:when> + <xsl:otherwise><xsl:value-of select="@method"/></xsl:otherwise> + </xsl:choose> + </xsl:attribute> + + <xsl:apply-templates select="bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="bebop:pageState" /> + + <div id="title"> + <h2 class="pageSubTitle"><xsl:value-of select="$title" /></h2> + <!--<xsl:if test="@name = 'newPostForm'">--> + <br/> + <!--</xsl:if>--> + </div> + <div class="content"> + <xsl:apply-templates select="forum:postForm" /> + <xsl:apply-templates select="forum:postConfirm" /> + <xsl:apply-templates select="forum:postFormImages" /> + <xsl:apply-templates select="forum:postFormFiles" /> + + <div class="clearGroup"> + <div class="validationError"> + <xsl:apply-templates select="bebop:formErrors" /> + </div> + </div> + + <div class="clearGroup"> + <div class="forumButton"> + <br/> + <xsl:apply-templates select="bebop:boxPanel/bebop:cell/bebop:formWidget[@name = 'Cancel']" /> + <xsl:apply-templates select="bebop:boxPanel/bebop:cell/bebop:formWidget[@name = '<<_Back']" /> + <xsl:apply-templates select="bebop:boxPanel/bebop:cell/bebop:formWidget[@name = 'Next_>>']" /> + <xsl:apply-templates select="bebop:boxPanel/bebop:cell/bebop:formWidget[@name = 'Finish']" /> + <br/> + </div> + </div> + <br/> + </div> + </form> + </xsl:template> + + <xsl:template match="forum:postForm"> + <xsl:apply-templates select="bebop:formWidget[@type='hidden']" /> + + <div class="clearGroup"> + <div class="columnOne">Subject:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:formWidget[@name='subject']" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:formErrors[@id='subject']" /> + </div> + </div> + </div> + <div class="clearGroup"> + <div class="columnOne">Message:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:*[@name='message']" /> + <div class="validationError"> + <xsl:value-of select="bebop:formErrors[@id='message']/@message" disable-output-escaping="yes"/> + </div> + </div> + </div> + <!--<div class="clearGroup"> + <div class="columnOne">Format:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:select[@name='bodyType']" /> + </div> + </div>--> + <xsl:choose> + <xsl:when test="bebop:select[@name='postTopic']"> + <div class="clearGroup"> + <div class="columnOne">Topic:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:select[@name='postTopic']" /> + </div> + </div> + </xsl:when> + <xsl:when test="forum:message"> + <div id="plainText"> + Original Message: + </div> + <div class="postOdd"> + <fieldset> + <xsl:apply-templates select="forum:message" /> + </fieldset> + </div> + </xsl:when> + </xsl:choose> + + </xsl:template> + + <xsl:template match="bebop:formErrors"> + <xsl:value-of select="@message" /> + </xsl:template> + + <xsl:template match="forum:message"> + <div class="clearGroup"> + <div class="columnOneWide"> + <a href="mailto:{sender/primaryEmail}"> + <xsl:value-of select="sender/displayName" /> + </a> + </div> + <div class="columnTwo"> + <xsl:value-of select="subject" /> + <br/> + <xsl:text>Posted</xsl:text> + <xsl:value-of select="sent" /> + <div class="replyBar"> + <hr/> + </div> + <xsl:value-of select="body" disable-output-escaping="yes" /> + </div> + </div> + </xsl:template> + + <xsl:template match="forum:postConfirm"> + <xsl:call-template name="displayPost"> + <xsl:with-param name="preview" select="'true'" /> + </xsl:call-template> + </xsl:template> + + <xsl:template match="forum:postFormImages"> + <xsl:apply-templates select="bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="forum:attachedImages" /> + + + <div class="clearGroup"> + <div class="columnOne">Image:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:formWidget[@name='image']" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:formErrors[@id='image']" /> + </div> + </div> + </div> + <div class="clearGroup"> + <div class="columnOne">Description:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:textarea" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:formErrors[@id='imageDescription']" /> + </div> + </div> + </div> + <div class="submit"> + <div class="forumButton"> + <xsl:apply-templates select="bebop:formWidget[@name='Add_Image']" /> + </div> + </div> + </xsl:template> + + <xsl:template match="forum:attachedImages"> + + + <xsl:choose> + <xsl:when test="not(forum:image)"> + <div id="plainText"> + No images attached to this post + </div> + </xsl:when> + <xsl:otherwise> + + <xsl:for-each select="forum:image"> + <xsl:variable name="class"> + <xsl:choose> + <xsl:when test="position() mod 2"> + <xsl:text>postOdd</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>postEven</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + <div class="clearGroup"> + <fieldset class="{$class}"> + <div class="image"> + <img src="{$dispatcher-prefix}{@src}" alt="{@caption}" /> + </div> + <div class="imageName"> + <xsl:value-of select="@name"/> + </div> + <div class="imageDelete"> + <a href="{@deleteLink}">Remove</a> + </div> + </fieldset> + </div> + </xsl:for-each> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <xsl:template match="forum:postFormFiles"> + <xsl:apply-templates select="bebop:formWidget[@type='hidden']" /> + + <xsl:apply-templates select="forum:attachedFiles" /> + + <div id="plainText"> + <xsl:apply-templates select="bebop:label " /> + </div> + + <div class="clearGroup"> + <div class="columnOne">File:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:formWidget[@name='file']" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:formErrors[@id='file']" /> + </div> + </div> + </div> + <div class="clearGroup"> + <div class="columnOne">Description:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:textarea" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:formErrors[@id='fileDescription']" /> + </div> + </div> + </div> + <div class="submit"> + <div class="forumButton"> + <xsl:apply-templates select="bebop:formWidget[@name='Add_File']" /> + </div> + </div> + </xsl:template> + + <xsl:template match="forum:attachedFiles" > + <xsl:choose> + <xsl:when test="not(forum:file)"> + <div id="plainText"> + No files attached to this post + </div> + </xsl:when> + <xsl:otherwise> + <div class="clearGroup"> + <table class="fileTable"> + <tr> + <td><h5>File</h5></td> + <td><h5>Description</h5></td> + <td/> + </tr> + <xsl:for-each select="forum:file"> + <tr> + <td> + <xsl:variable name="encodedFileName"> + <xsl:call-template name="url-encode"> + <xsl:with-param name="str" select="@name"/> + </xsl:call-template> + </xsl:variable> + + <a href="{$dispatcher-prefix}{@url}&file=/{$encodedFileName}" target = "_blank"><xsl:value-of select="@name"/></a></td> + <td> + <xsl:if test="not(contains(@description, 'nbsp'))"> + <xsl:value-of select="@description"/> + </xsl:if> + </td> + <td><a href="{@deleteLink}">Remove</a></td> + </tr> + </xsl:for-each> + </table> + <br/> + </div> + </xsl:otherwise> + + </xsl:choose> + + </xsl:template> + + <xsl:template match="threadOptions"> + <div id="threadOptions"> + <xsl:apply-templates /> + </div> + </xsl:template> + + <xsl:template match="bebop:form[@name='newPostForm']"> + <xsl:call-template name="ForumPostForm"> + <xsl:with-param name="title"> + <xsl:choose> + <xsl:when test="forum:postForm"> + <xsl:text>Post New Message</xsl:text> + </xsl:when> + <xsl:when test="forum:postFormImages"> + <xsl:text>Add Images (optional)</xsl:text> + </xsl:when> + <xsl:when test="forum:postFormFiles"> + <xsl:text>Add Files (optional)</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>Preview New Message</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <xsl:template match="bebop:form[@name='editPostForm']"> + <xsl:call-template name="ForumPostForm"> + <xsl:with-param name="title"> + <xsl:choose> + <xsl:when test="forum:postForm"> + <xsl:text>Edit Message</xsl:text> + </xsl:when> + <xsl:when test="forum:postFormImages"> + <xsl:text>Edit Images</xsl:text> + </xsl:when> + <xsl:when test="forum:postFormFiles"> + <xsl:text>Edit Files</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>Preview Changes</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <xsl:template match="bebop:form[@name='replyPostForm']"> + <xsl:call-template name="ForumPostForm"> + <xsl:with-param name="title"> + <xsl:choose> + <xsl:when test="forum:postForm"> + <xsl:text>Post reply</xsl:text> + </xsl:when> + <xsl:when test="forum:postFormImages"> + <xsl:text>Add Images (optional)</xsl:text> + </xsl:when> + <xsl:when test="forum:postFormFiles"> + <xsl:text>Add Files (optional)</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text>Preview Reply</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <xsl:template match="bebop:form[@name='categoryAdd']"> + <xsl:param name="title" select="'Create New Topic'" /> + <div class="content"> + <form action="{@action}" name="{@name}" method="get"> + <xsl:apply-templates select="bebop:gridPanel/bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="bebop:pageState" /> + + <div id="title"> + <h2 class="pageSubTitle"><xsl:value-of select="$title" /></h2> + <br/> + </div> + + <div class="clearGroup"> + <div class="columnOne">Name:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:formWidget[@name='name']" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:formErrors[@id='name']" /> + </div> + </div> + </div> + <div class="clearGroup"> + <div class="columnOne">Description:</div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:textarea" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:formErrors[@id='description']" /> + </div> + </div> + </div> + <div class="submit"> + <div class="forumButton"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:formWidget[@type='submit']" /> + <br/> + </div> + </div> + </form> + </div> + </xsl:template> + + <xsl:template match="bebop:form[@name='postRejectionForm']"> + <xsl:param name="title" select="'Reject Post'" /> + + <form action="{@action}" name="{@name}" method="get"> + <xsl:apply-templates select="bebop:gridPanel/bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="bebop:pageState" /> + + <div id="title"> + <h2 class="pageSubTitle"><xsl:value-of select="$title" /></h2> + <br/> + </div> + <div class="content"> + <xsl:choose> + <xsl:when test="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:textarea" > + <div class="clearGroup"> + <div class="columnOne"><xsl:value-of select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:formWidget[@name='recipient']/@metadata.label" /></div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:formWidget[@name='recipient']" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:formErrors[@id='recipient']" /> + </div> + </div> + </div> + <div class="clearGroup"> + <div class="columnOne"><xsl:value-of select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:textarea[@name='bodyText']/@metadata.label" /></div> + <div class="columnTwo"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:textarea[@name='bodyText']" /> + <div class="validationError"> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:formErrors[@id='bodyText']" /> + </div> + </div> + </div> + </xsl:when> + <xsl:otherwise> + <xsl:for-each select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:label" > + <div class="clearGroup"> + <xsl:call-template name="br-replace" > + <xsl:with-param name="text" select="."> + </xsl:with-param> + </xsl:call-template> + + </div> + </xsl:for-each> + </xsl:otherwise> + </xsl:choose> + + <div class="clearGroup"> + <div class="forumButton"> + <br/> + <xsl:apply-templates select="bebop:gridPanel/bebop:panelRow/bebop:cell/bebop:boxPanel/bebop:cell/bebop:formWidget[@type='submit']" /> + <br/> + </div> + </div> + </div> + </form> + </xsl:template> + + <xsl:template match="bebop:form[@name='setupForm']"> + <div class="content"> + <form action="{@action}" name="{@name}" method="get"> + <xsl:apply-templates select="forum:setup/bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="bebop:pageState" /> + + <div class="clearGroup"> + <xsl:value-of select="forum:setup/bebop:formWidget[@name='title']/@metadata.label"/> + </div> + <div class="clearGroup"> + <xsl:apply-templates select="forum:setup/bebop:formWidget[@name='title']"/> + </div> + <div class="clearGroup"> + <xsl:value-of select="forum:setup/bebop:textarea/@metadata.label"/> + </div> + <div class="clearGroup"> + <xsl:apply-templates select="forum:setup/bebop:textarea"/> + </div> + <div class="clearGroup"> + <xsl:apply-templates select="descendant::bebop:checkbox" /> + + <div class="expiry"> + <xsl:apply-templates select="descendant::bebop:formWidget[@name='expiry']" /> + <xsl:value-of select="descendant::bebop:formWidget[@name='expiry']/@metadata.label"/> + </div> + </div> + + <div class="clearGroup"> + <div class="forumButton"> + <xsl:apply-templates select="forum:setup/bebop:boxPanel/bebop:cell/bebop:formWidget[@name='save']" /> + <br/> + </div> + </div> + </form> + </div> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'adminMemberUserPicker']" mode="show"> + <xsl:call-template name="permissionForm" /> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'adminMemberUserPicker']"> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'memberUserPicker']" mode="show"> + <xsl:call-template name="permissionForm" /> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'memberUserPicker']"> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'threadCreateMemberUserPicker']" mode="show"> + <xsl:call-template name="permissionForm" /> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'threadCreateMemberUserPicker']"> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'threadReplyMemberUserPicker']" mode="show"> + <xsl:call-template name="permissionForm" /> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'threadReplyMemberUserPicker']"> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'readerMemberUserPicker']" mode="show"> + <xsl:call-template name="permissionForm" /> + </xsl:template> + + <xsl:template match="bebop:form[@id = 'readerMemberUserPicker']"> + </xsl:template> + + <xsl:template name="permissionForm"> + <form action="{@action}" name="{@name}" method="get"> + <xsl:apply-templates select="bebop:formWidget[@type='hidden']" /> + <xsl:apply-templates select="bebop:pageState" /> + <xsl:apply-templates /> + </form> + </xsl:template> + + +<!-- *********************** LINK TEMPLATES ******************** --> + + + <xsl:template match="bebop:link[bebop:label/text() = 'New thread']"> + <br/> + <div id="newTopicLink"> + <a href="{@href}">Start New Thread</a> + </div> + </xsl:template> + + <xsl:template match="bebop:link[bebop:label/text() = 'New topic']"> + <div id="newTopicLink"> + <a href="{@href}">Create New Topic</a> + </div> + </xsl:template> + + <xsl:template match="bebop:link[bebop:label/text() = 'View all threads']"> + <div id="viewAllTopicsLink"> + <a href="{@href}">Back to Thread List</a> + </div> + </xsl:template> + + <xsl:template match="bebop:link[bebop:label/text() = 'Subscribe to thread']"> + <div id="watchTopicLink"> + <a href="{@href}">Watch this Thread</a> + </div> + </xsl:template> + + <xsl:template match="bebop:link[bebop:label/text() = 'Unsubscribe to thread']"> + <div id="info"> + <xsl:text>You are watching this thread. To stop watching this thread, click "Stop Watching Thread" below.</xsl:text> + </div> + <div id="watchTopicLink"> + <a href="{@href}">Stop Watching Thread</a> + </div> + </xsl:template> + + +<xsl:template name="br-replace"> + <xsl:param name="text"/> + <xsl:variable name="cr" select="'
'"/> + <xsl:choose> + <!-- If the value of the $text parameter contains a carriage return... --> + <xsl:when test="contains($text,$cr)"> + <!-- Return the substring of $text before the carriage return --> + <xsl:value-of select="substring-before($text,$cr)" disable-output-escaping="yes"/> + <!-- And construct a <br/> element --> + <br/> + <!-- + | Then invoke this same br-replace template again, passing the + | substring *after* the carriage return as the new "$text" to + | consider for replacement + +--> + <xsl:call-template name="br-replace"> + <xsl:with-param name="text" select="substring-after($text,$cr)"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$text" disable-output-escaping="yes"/> + </xsl:otherwise> + </xsl:choose> +</xsl:template> +</xsl:stylesheet> + + Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/archived-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/archived-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/back-to-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/back-to-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/delete-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/delete-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/edit-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/edit-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/info-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/info-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/post-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/post-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/read.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/read.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/reply-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/reply-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/watch-16x16.gif =================================================================== (Binary files differ) Property changes on: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example/images/forum/watch-16x16.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream |
Author: chrisg23 Date: 2007-09-17 11:00:10 +0200 (Mon, 17 Sep 2007) New Revision: 1628 Added: aplaws/trunk/ccm-forum/doc/new-features.txt aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/MyForumsPortlet.pdl aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_anonymous_option.sql aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_file_attachments.sql aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_introduction.sql aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_no_category_posts.sql aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_groups.sql aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_image_uploads.sql aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_privileges.sql aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_thread_subscriber.sql aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/oracle-se-6.5.0-6.5.1.sql aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/postgres-6.5.0-6.5.1.sql aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumPageBuilder.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumPageFactory.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/PageBuilder.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/PostFileAttachment.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/PostFileAttachmentURLFinder.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/PostFinder.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/PostImageAttachment.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/RemoveUnattachedAssetsScheduler.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/RemoveUnattachedAssetsTask.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ThreadPageBuilder.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/portlet/MyForumsPortlet.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/search/ aplaws/trunk/ccm-forum/src/com/arsdigita/forum/search/FileAttachmentMetadataProvider.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/search/PostMetadataProvider.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/search/TextContentProvider.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/search/XMLContentProvider.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/AttachedFilesStep.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/AttachedFilesTable.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ConfirmStep.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ImagesStep.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ImagesTable.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/PostTextStep.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ReplyToPostTextStep.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/RootPostForm.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/RootPostTextStep.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/AdminEditPane.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/PermissionsView.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/ReaderEditPane.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/SetupView.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/ThreadCreatorEditPane.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/ThreadResponderEditPane.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/upgrade/ aplaws/trunk/ccm-forum/src/com/arsdigita/forum/upgrade/CreateContainerGroups.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/upgrade/CreateGroupsAndPortletType.java Removed: aplaws/trunk/ccm-forum/etc/enterprise.init.in aplaws/trunk/ccm-forum/src/com/arsdigita/forum/BboardDispatcher.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/NewPostForm.java aplaws/trunk/ccm-forum/web/WEB-INF/bebop-define.tld aplaws/trunk/ccm-forum/web/WEB-INF/bebop-show.tld Modified: aplaws/trunk/ccm-forum/application.xml aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Forum.pdl aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Post.pdl aplaws/trunk/ccm-forum/src/WEB-INF/resources/forum-adapters.xml aplaws/trunk/ccm-forum/src/ccm-forum.upgrade aplaws/trunk/ccm-forum/src/com/arsdigita/forum/DailySubscription.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Forum.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumContext.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumServlet.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumSubscription.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Initializer.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Loader.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Post.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Subscription.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ThreadSubscription.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/portlet/RecentPostingsPortlet.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/CategoryAddForm.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/CategoryView.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/CategoryWidget.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/Constants.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/EditPostForm.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ForumAlertsView.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ForumComponent.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ForumResources.properties aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ForumUserView.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/MessageView.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/PostForm.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ReplyToPostForm.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ThreadAlertsList.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ThreadComponent.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ThreadDisplay.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/ThreadList.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/TopicList.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/TopicSelector.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/GroupMemberDisplay.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/GroupMemberPicker.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/ModeratorEditPane.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/RejectionForm.java aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ui/admin/UserPicker.java aplaws/trunk/ccm-forum/web/WEB-INF/web.xml aplaws/trunk/ccm-forum/web/packages/forum/xsl/forum.xsl Log: Sourceforge patch 1689988 - rather a large commit, but in fact causes very little visible change. Large single commit is necessary because all these changes are essentially atomic. Modified: aplaws/trunk/ccm-forum/application.xml =================================================================== --- aplaws/trunk/ccm-forum/application.xml 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/application.xml 2007-09-17 09:00:10 UTC (rev 1628) @@ -2,11 +2,12 @@ <ccm:application xmlns:ccm="http://ccm.redhat.com/ccm-project" name="ccm-forum" prettyName="Forum" - version="6.5.0" + version="6.5.2" release="1"> <ccm:dependencies> - <ccm:requires name="ccm-core" version="6.2.0" relation="ge"/> - <ccm:requires name="ccm-cms" version="6.2.0" relation="ge"/> + + <ccm:requires name="ccm-core" version="6.5.3"/> + <ccm:requires name="ccm-cms" version="6.5.3"/> </ccm:dependencies> <ccm:contacts> <ccm:contact uri="http://www.redhat.com/software/rhea" type="website"/> Added: aplaws/trunk/ccm-forum/doc/new-features.txt =================================================================== --- aplaws/trunk/ccm-forum/doc/new-features.txt (rev 0) +++ aplaws/trunk/ccm-forum/doc/new-features.txt 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,86 @@ +Main new features included in this release are: + +Fine grained forum permissions +Allow file attachments and images in posts +Contents of posts are searchable +New portlet type lising links to all forums user has read access to +Allow automatic thread subscription for thread starter (useful for noticeboard type forums) +Forum admin can prevent posts with no topic +Expiry can be specified for all types of forum +Reply count calculation fixed +HTML Editor can be used for posts +Subscription emails can (and should) be sent as html emails) + +Other minor changes, fixes and configurable features are also included. +Check new config parameter descriptions in com.arsdigita.forum.ForumConfig_parameter.properties + +After deployment, to install new features, either get rid of existing forum app, and load new version, OR + +ccm upgrade ccm-forum --from-version 6.5.0 --to-version 6.5.1 +ccm upgrade ccm-forum --from-version 6.5.1 --to-version 6.5.2 +ccm-run com.arsdigita.london.search.Reindexer --object-type com.arsdigita.forum.Post + + + +When developing this, I tried to ensure that a basic deployment will not affect the XML +that is produced by the current forum application. + +I have tested the new implementation with Coventry's theme. There is one theme amendment required (because of a bug fix). +In current release, the drop down list for filtering threads by category has the same name as the drop down list for selecting a category when making a new post. This means that the selected value carries through . ie if you have filtered the threads then make a new post, the new post has the filter category selected as default. More importantly, if you make a post and assign a category, then the thread list is filtered after you complete the post. + +To fix the bug, I renamed the drop down list in the post form from topic to PostTopic. This means the category drop down is not visible until the two references to @name='topic' in the forum:postForm template are changed to @name='postTopic' + +Only other visible changes if the patch is applied without changing config +are: + +Reply count only includes posts that have been approved + +Posts are searchable from the main site search. This is a change from the current behaviour . if not desired then it could be made configurable. + +Moderation form and emails are formatted slightly differently + +recent forum postings portlet prevents users creating new forum application . +only site admin can do this. Also filters forums to those that the user has +read access on. + +New portlet . my forums. Lists links to forums that user has read access to. + +The other non-visble effect is that the wizard uses java session rather than +hidden fields. This approach makes it much easier to include links in form +steps (eg remove an uploaded file etc). The impact is that users in a load +balanced environment need to consider session affinity. In our production +environment, this is accomplished by the server that sprays requests to the +various back end servers. It identifies each browser session and routes all +their requests to the same back end server. Hence there is no replication of +session - the user is guaranteed to end up at the server that holds their java +session. As a result, when a backend server is stopped, there is a risk that a +user with session on that server in the middle of creating a post will lose +information entered up to that point. On our site we live with this small risk +but mitigate it by restarting servers at unsociable hours where possible. + +I have put examle theme files in +ccm-ldn-aplaws/web/__ccm__/themes/aplaws/forum-example: forum-index.xsl, +forum-index.css and the contents of images/forum + +To have a look at a forum using these files, copy the xsl and css to the top +level of your theme and make a new forum directory in images and copy the +images there. I.m afraid these files have continued the existing format of +putting all the templates in a single file, so they are not the most easily +maintained, and I make no guarantees about the content, but they do work. and +if required they may be used as a basis for your own look and feel. + + +There is a separate application - ccm-categorised-forum that adds category path and menu components to the front end, +and adds a categorisation tab for admin users. Load this application if you would like this functionality. + +When a forum is created, only site admin can access the admin tabs, +until you assign users/groups to the forum admin group under the permissions tab. + +Next things I plan to do are: + +list pending and reapprove posts under the moderation tab +create a user info page so users can write a bit about themselves +allow thread branching so administrators can split long threads or threads that have strayed from the point. +Allow different thread views - paginated full details, or tree of titles with one post visible (a la sourceforge) + +ch...@we... Deleted: aplaws/trunk/ccm-forum/etc/enterprise.init.in Modified: aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Forum.pdl =================================================================== --- aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Forum.pdl 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Forum.pdl 2007-09-17 09:00:10 UTC (rev 1628) @@ -32,8 +32,16 @@ Boolean [1..1] isModerated = forum_forums.is_moderated; Boolean [1..1] isNoticeboard = forum_forums.is_noticeboard; + component Group [0..1] adminGroup = + join forum_forums.admin_group_id to groups.group_id; component Group [0..1] moderationGroup = join forum_forums.mod_group_id to groups.group_id; + component Group [0..1] threadCreateGroup = + join forum_forums.create_group_id to groups.group_id; + component Group [0..1] threadRespondGroup = + join forum_forums.respond_group_id to groups.group_id; + component Group [0..1] readGroup = + join forum_forums.read_group_id to groups.group_id; component ForumSubscription[0..n] subscriptions = join forum_forums.forum_id to forum_subscriptions.forum_id; @@ -44,7 +52,12 @@ join forum_forums.lifecycle_definition_id to lifecycle_definitions.definition_id; BigDecimal [0..1] expireAfter = forum_forums.expire_after; - + Boolean [1..1] fileAttachmentsAllowed = forum_forums.file_attachments_allowed; + Boolean [1..1] imageUploadsAllowed = forum_forums.image_uploads_allowed; + Boolean [1..1] autoSubscribeThreadStarter = forum_forums.subscribe_thread_starter; + Boolean [1..1] noCategoryPostsAllowed = forum_forums.no_category_posts_allowed; + Boolean [1..1] anonymousPostsAllowed = forum_forums.anonymous_posts_allowed; + String [0..1] introduction = forum_forums.introduction VARCHAR(4000); reference key (forum_forums.forum_id); } Added: aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/MyForumsPortlet.pdl =================================================================== --- aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/MyForumsPortlet.pdl (rev 0) +++ aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/MyForumsPortlet.pdl 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,30 @@ +// +// Copyright (C) 2007 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 +// +// $Id: RecentPostingsPortlet.pdl 285 2005-02-22 00:29:02Z sskracic $ +// $DateTime: 2004/08/17 23:26:27 $ +model com.arsdigita.forum; + +import com.arsdigita.portal.Portlet; + +// lists links to forums for which user has read access + +object type MyForumsPortlet extends Portlet { + +// no attributes for this one + +} Modified: aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Post.pdl =================================================================== --- aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Post.pdl 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/pdl/com/arsdigita/forum/Post.pdl 2007-09-17 09:00:10 UTC (rev 1628) @@ -21,7 +21,10 @@ import com.arsdigita.messaging.ThreadedMessage; import com.arsdigita.kernel.Party; +import com.arsdigita.cms.FileAsset; +import com.arsdigita.cms.ImageAsset; + object type Post extends com.arsdigita.messaging.ThreadedMessage { String [1..1] status = forum_posts.status VARCHAR(20); @@ -29,7 +32,32 @@ aggressive load(moderator.id); } +object type PostFileAttachment extends FileAsset { + Integer[0..1] fileOrder = forum_post_files.file_order INTEGER; + reference key (forum_post_files.file_id); +} + +object type PostImageAttachment extends ImageAsset { + Integer[0..1] imageOrder = forum_post_images.image_order INTEGER; + reference key (forum_post_images.image_id); +} + association { + + composite Post[0..1] fileMessage = join forum_post_files.post_id to forum_posts.post_id; + component PostFileAttachment[0..n] files = join forum_posts.post_id to forum_post_files.post_id; + +} + +association { + + composite Post[0..1] imageMessage = join forum_post_images.post_id to forum_posts.post_id; + component PostImageAttachment[0..n] images = join forum_posts.post_id to forum_post_images.post_id; + +} + + +association { Party [0..1] moderator = join forum_posts.moderator to parties.party_id; Post [0..1] moderatedMessage = join parties.party_id to forum_posts.moderator; } @@ -53,3 +81,20 @@ id = message_threads.thread_id; } } + +// Subquery for filtering out threads where root post is of a +// particular status (eg unconfirmed) +// +//query threadStatus { + // BigDecimal id; +// + // do { + // select m.thread_id + // from forum_posts f, + // message_threads m + // where m.root_id = f.post_id + // and f.status <> :status2 + // } map { + // id = message_threads.thread_id; + // } +// } Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_anonymous_option.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_anonymous_option.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_anonymous_option.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,3 @@ +alter table forum_forums add (anonymous_posts_allowed CHAR(1)); + +update forum_forums set anonymous_posts_allowed = 0; \ No newline at end of file Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_file_attachments.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_file_attachments.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_file_attachments.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,21 @@ +create table forum_post_files + (post_id NUMBER, + file_id NUMBER not null, + file_order NUMBER); + + + +alter table forum_post_files add + constraint FORU_POS_FILES_FILE_ID_F_XBKED foreign key (file_id) + references cms_files(file_id); + +alter table forum_post_files add + constraint FORU_POS_FILES_POST_ID_F_K0XJQ foreign key (post_id) + references forum_posts(post_id); + +alter table forum_forums add (file_attachments_allowed CHAR(1)); + +update forum_forums set file_attachments_allowed = 0; + + + Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_introduction.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_introduction.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_introduction.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,2 @@ +ALTER TABLE FORUM_FORUMS ADD ( + INTRODUCTION VARCHAR2(4000) ); \ No newline at end of file Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_no_category_posts.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_no_category_posts.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_forum_no_category_posts.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,3 @@ +alter table forum_forums add (no_category_posts_allowed CHAR(1)); + +update forum_forums set no_category_posts_allowed = 1; Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_groups.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_groups.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_groups.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,20 @@ +alter table forum_forums +add (admin_group_id NUMBER, + create_group_id NUMBER, + respond_group_id NUMBER, + read_group_id NUMBER); + + +alter table forum_forums add + constraint foru_foru_admi_grou_id_f_k0nw6 foreign key (admin_group_id) + references groups(group_id); +alter table forum_forums add + constraint foru_foru_crea_grou_id_f_f7x57 foreign key (create_group_id) + references groups(group_id); +alter table forum_forums add + constraint foru_foru_respo_gro_id_f_rnofz foreign key (respond_group_id) + references groups(group_id); +alter table forum_forums add + constraint foru_forum_rea_grou_id_f_itati foreign key (read_group_id) + references groups(group_id); + Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_image_uploads.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_image_uploads.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_image_uploads.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,21 @@ +create table forum_post_images + (post_id NUMBER, + image_id NUMBER not null, + image_order NUMBER); + + + +alter table forum_post_images add + constraint FORU_POS_IMAGE_IMAG_ID_F_WYRXA foreign key (image_id) + references cms_images(image_id); + +alter table forum_post_images add + constraint FORU_POS_IMAGE_POST_ID_F_1HH02 foreign key (post_id) + references forum_posts(post_id); + +alter table forum_forums add (image_uploads_allowed CHAR(1)); + +update forum_forums set image_uploads_allowed = 0; + + + Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_privileges.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_privileges.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_privileges.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,18 @@ +insert into acs_privileges (privilege) +values ('forum_read'); + +insert into acs_privileges (privilege) +values ('forum_create_thread'); + +insert into acs_privileges (privilege) +values ('forum_respond'); + +insert into acs_privilege_hierarchy (privilege, child_privilege) +values ('forum_moderation', 'forum_create_thread'); + +insert into acs_privilege_hierarchy (privilege, child_privilege) +values ('forum_create_thread', 'forum_respond'); + +insert into acs_privilege_hierarchy (privilege, child_privilege) +values ('forum_respond', 'forum_read'); + Added: aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_thread_subscriber.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_thread_subscriber.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/default/upgrade/6.5.0-6.5.1/add_thread_subscriber.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,5 @@ +alter table forum_forums add (subscribe_thread_starter CHAR(1)); + +update forum_forums set subscribe_thread_starter = 0; + + Added: aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/oracle-se-6.5.0-6.5.1.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/oracle-se-6.5.0-6.5.1.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/oracle-se-6.5.0-6.5.1.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,8 @@ +@@ ../default/upgrade/6.5.0-6.5.1/add_privileges.sql +@@ ../default/upgrade/6.5.0-6.5.1/add_groups.sql +@@ ../default/upgrade/6.5.0-6.5.1/add_thread_subscriber.sql +@@ ../default/upgrade/6.5.0-6.5.1/add_file_attachments.sql +@@ ../default/upgrade/6.5.0-6.5.1/add_forum_introduction.sql +@@ ../default/upgrade/6.5.0-6.5.1/add_image_uploads.sql +@@ ../default/upgrade/6.5.0-6.5.1/add_forum_no_category_posts.sql +@@ ../default/upgrade/6.5.0-6.5.1/add_anonymous_option.sql Added: aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/postgres-6.5.0-6.5.1.sql =================================================================== --- aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/postgres-6.5.0-6.5.1.sql (rev 0) +++ aplaws/trunk/ccm-forum/sql/ccm-forum/upgrade/postgres-6.5.0-6.5.1.sql 2007-09-17 09:00:10 UTC (rev 1628) @@ -0,0 +1,10 @@ +\i ../default/upgrade/6.5.0-6.5.1/add_thread_subscriber.sql +\i ../default/upgrade/6.5.0-6.5.1/add_privileges.sql +\i ../default/upgrade/6.5.0-6.5.1/add_groups.sql +\i ../default/upgrade/6.5.0-6.5.1/add_privileges.sql +\i ../default/upgrade/6.5.0-6.5.1/add_file_attachments.sql +\i ../default/upgrade/6.5.0-6.5.1/add_forum_introduction.sql +\i ../default/upgrade/6.5.0-6.5.1/add_image_uploads.sql +\i ../default/upgrade/6.5.0-6.5.1/add_forum_no_category_posts.sql +\i ../default/upgrade/6.5.0-6.5.1/add_anonymous_option.sql + Modified: aplaws/trunk/ccm-forum/src/WEB-INF/resources/forum-adapters.xml =================================================================== --- aplaws/trunk/ccm-forum/src/WEB-INF/resources/forum-adapters.xml 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/src/WEB-INF/resources/forum-adapters.xml 2007-09-17 09:00:10 UTC (rev 1628) @@ -79,6 +79,8 @@ <xrd:property name="/object/root"/> <xrd:property name="/object/categories"/> <xrd:property name="/object/sender"/> + <xrd:property name="/object/images"/> + <xrd:property name="/object/files"/> </xrd:associations> </xrd:adapter> </xrd:context> @@ -132,4 +134,77 @@ </xrd:adapter> </xrd:context> + + + <xrd:context name="com.arsdigita.forum.ui.ConfirmStep"> + <xrd:adapter objectType="com.arsdigita.forum.PostFileAttachment"> + <xrd:attributes rule="exclude"/> + </xrd:adapter> + + <xrd:adapter objectType="com.arsdigita.forum.PostImageAttachment"> + <xrd:attributes rule="exclude"/> + </xrd:adapter> + + + <!-- <xrd:adapter objectType="com.arsdigita.forum.Post" + formatter="com.arsdigita.forum.ui.MessageXMLFormatter"> + + <xrd:attributes rule="exclude"> + <xrd:property name="/object/id"/> + <xrd:property name="/object/objectType"/> + <xrd:property name="/object/defaultDomainClass"/> + <xrd:property name="/object/displayName"/> + <xrd:property name="/object/replyTo"/> + <xrd:property name="/object/objectID"/> + <xrd:property name="/object/messageID"/> + <xrd:property name="/object/root"/> + <xrd:property name="/object/inReplyTo"/> + + <xrd:property name="/object/categories/objectType"/> + <xrd:property name="/object/categories/defaultDomainClass"/> + <xrd:property name="/object/categories/displayName"/> + <xrd:property name="/object/categories/isAbstract"/> + <xrd:property name="/object/categories/isEnabled"/> + <xrd:property name="/object/categories/defaultAncestors"/> + + <xrd:property name="/object/sender/id"/> + <xrd:property name="/object/sender/objectType"/> + <xrd:property name="/object/sender/defaultDomainClass"/> + </xrd:attributes> + + <xrd:associations rule="include"> + <xrd:property name="/object/root"/> + <xrd:property name="/object/categories"/> + <xrd:property name="/object/sender"/> + <xrd:property name="/object/images"/> + <xrd:property name="/object/files"/> + </xrd:associations> + </xrd:adapter> --> + </xrd:context> + + <xrd:context name="com.arsdigita.forum.search.PostMetadataProvider"> + <xrd:adapter objectType="com.arsdigita.forum.Post"> + <xrd:attributes rule="include"> + <xrd:property name="/object/subject"/> + <xrd:property name="/object/body"/> + <xrd:property name="/object/images/description"/> + + </xrd:attributes> + <xrd:associations rule="include"> + <xrd:property name="/object/images"/> + </xrd:associations> + </xrd:adapter> + </xrd:context> + + <xrd:context name="com.arsdigita.forum.search.FileAttachmentMetadataProvider"> + <xrd:adapter objectType="com.arsdigita.forum.PostFileAttachment"> + <xrd:attributes rule="exclude"> + + </xrd:attributes> + + </xrd:adapter> + </xrd:context> + + + </xrd:adapters> Modified: aplaws/trunk/ccm-forum/src/ccm-forum.upgrade =================================================================== --- aplaws/trunk/ccm-forum/src/ccm-forum.upgrade 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/src/ccm-forum.upgrade 2007-09-17 09:00:10 UTC (rev 1628) @@ -5,4 +5,11 @@ <version from="1.4.3" to="1.4.4"> <script sql="ccm-forum/upgrade/::database::-1.4.3-1.4.4.sql"/> </version> + <version from="6.5.0" to="6.5.1"> + <script sql="ccm-forum/upgrade/::database::-6.5.0-6.5.1.sql"/> + <script class="com.arsdigita.forum.upgrade.CreateGroupsAndPortletType"/> + </version> + <version from="6.5.1" to="6.5.2"> + <script class="com.arsdigita.forum.upgrade.CreateContainerGroups"/> + </version> </upgrade> Deleted: aplaws/trunk/ccm-forum/src/com/arsdigita/forum/BboardDispatcher.java Modified: aplaws/trunk/ccm-forum/src/com/arsdigita/forum/DailySubscription.java =================================================================== --- aplaws/trunk/ccm-forum/src/com/arsdigita/forum/DailySubscription.java 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/src/com/arsdigita/forum/DailySubscription.java 2007-09-17 09:00:10 UTC (rev 1628) @@ -36,7 +36,7 @@ * * @author Kevin Scaldeferri (ke...@ar...) * - * @version $Revision: #8 $ $Author$ $DateTime: 2004/08/17 23:26:27 $ + * @version $Revision: 1.1 $ $Author$ $DateTime: 2004/08/17 23:26:27 $ */ public class DailySubscription extends ForumSubscription { @@ -94,6 +94,11 @@ return BASE_DATA_OBJECT_TYPE; } + + public String getSubscriptionGroupName() { + return "Daily Digest Subscription Group"; + } + private Digest getDigest() { if (m_digest == null) { DataObject digestData = (DataObject) get(DIGEST); @@ -115,6 +120,11 @@ public void sendNotification(Post post) { Notification notification = new Notification(getGroup(), post); notification.setDigest(getDigest()); + if (Forum.getConfig().deleteNotifications()) { +// make sure we don't delete the post itself!!! + notification.setMessageDelete(Boolean.FALSE); + notification.setIsPermanent(Boolean.FALSE); + } notification.save(); } Modified: aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Forum.java =================================================================== --- aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Forum.java 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/src/com/arsdigita/forum/Forum.java 2007-09-17 09:00:10 UTC (rev 1628) @@ -18,6 +18,10 @@ */ package com.arsdigita.forum; +import java.math.BigDecimal; + +import org.apache.log4j.Logger; + import com.arsdigita.categorization.Category; import com.arsdigita.cms.lifecycle.LifecycleDefinition; import com.arsdigita.domain.DataObjectNotFoundException; @@ -37,27 +41,27 @@ import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.DataQuery; import com.arsdigita.persistence.Filter; +import com.arsdigita.persistence.FilterFactory; import com.arsdigita.persistence.OID; import com.arsdigita.persistence.Session; import com.arsdigita.persistence.SessionManager; import com.arsdigita.util.Assert; import com.arsdigita.web.Application; -import java.math.BigDecimal; -import org.apache.log4j.Logger; /** * The Forum class represents a discussion forum. * * @author Kevin Scaldeferri (ke...@ar...) * - * @version $Revision: #25 $ $Author$ $DateTime: 2004/08/17 23:26:27 $ + * @version $Revision: 1.7 $ $Author$ $DateTime: 2004/08/17 23:26:27 $ */ public class Forum extends Application { + public static final String THREAD_SUBSCRIPTION_GROUPS_NAME = "Thread Subscription Groups"; public static final String versionId = - "$Id$" + - "$Author$" + - "$DateTime: 2004/08/17 23:26:27 $"; + "$Id$" + + "$Author$" + + "$DateTime: 2004/08/17 23:26:27 $"; private static ForumConfig s_config = new ForumConfig(); @@ -76,18 +80,48 @@ "com.arsdigita.forum.Forum"; public static final String PACKAGE_TYPE = "forum"; - - public static final String FORUM_MODERATION_PRIVILEGE - = "forum_moderation"; - + ////// + //Forum specific privileges + ///// + public static final String FORUM_MODERATION_PRIVILEGE = "forum_moderation"; + public static final String CREATE_THREAD_PRIVILEGE = "forum_create_thread"; + public static final String RESPOND_TO_THREAD_PRIVILEGE = "forum_respond"; + // separate read privilege required because all public users + // have READ on homepage, which is parent of forum, hence + // everyone inherits READ cg + // + // note in hindsight, I have stopped homepage being set as + // permission context for forum, because site search checks + // for READ privilege anyway, and so search results were being + // returned for non public posts. This means there is no longer + // any need for a separate forum_read privilege, though it + // does no harm. Now removed + // public static final String FORUM_READ_PRIVILEGE = "forum_read"; + + /////// + // pdl forum attribute/association names + /////// private static final String POSTS = "posts"; private static final String SUBSCRIPTIONS = "subscriptions"; private static final String MODERATION = "isModerated"; private static final String NOTICEBOARD = "isNoticeboard"; + private static final String ADMIN_GROUP = "adminGroup"; private static final String MODERATION_GROUP = "moderationGroup"; + private static final String THREAD_CREATE_GROUP = "threadCreateGroup"; + private static final String THREAD_RESPONDER_GROUP = "threadRespondGroup"; + private static final String READ_GROUP = "readGroup"; private static final String CATEGORY = "category"; private static final String EXPIRE_AFTER = "expireAfter"; private static final String LIFECYCLE_DEFINITION = "lifecycleDefinition"; + // additional attributes added chr...@we... + private static final String ALLOW_FILE_ATTACHMENTS = + "fileAttachmentsAllowed"; + private static final String ALLOW_IMAGE_UPLOADS = "imageUploadsAllowed"; + private static final String AUTOSUBSCRIBE_THREAD_STARTER = + "autoSubscribeThreadStarter"; + private static final String INTRODUCTION = "introduction"; + private static final String NO_CATEGORY_POSTS = "noCategoryPostsAllowed"; + private static final String ANONYMOUS_POSTS = "anonymousPostsAllowed"; public Forum(DataObject data) { super(data); @@ -113,16 +147,26 @@ * category for the Forum in the event that the Forum should be * categorized. * This also sets up instant and daily subscriptions on the Forum. - * The default for moderation is false + * The default for moderation is false. + * + * Also sets default values for other forum settings. These can be + * amended under the setup tab in the ui */ public static Forum create(String urlName, String title, Application parent, boolean moderated) { s_log.debug("creating forum " + title); Forum forum = (Forum) Application.createApplication - (BASE_DATA_OBJECT_TYPE, urlName, title, parent); + (BASE_DATA_OBJECT_TYPE, urlName, title, parent, true); forum.setModerated(moderated); + // default settings ensure legacy forum users do not + // see any change chr...@we... + forum.setAllowFileAttachments(false); + forum.setAllowImageUploads(false); + forum.setAutoSubscribeThreadCreator(false); + forum.setNoCategoryPostsAllowed(true); + forum.setAnonymousPostsAllowed(false); return forum; } @@ -151,6 +195,22 @@ } /** + * Set introduction + */ + public void setIntroduction(String introduction) { + set(INTRODUCTION, introduction); + } + + /** + * @return introduction + */ + + public String getIntroduction() { + return (String) get(INTRODUCTION); + + } + + /** * creates a Root Category for the forum. */ private Category createRootCategory() { @@ -162,7 +222,11 @@ return category; } - private void createModerationGroup() { + private void createGroups() { + Group administrators = new Group(); + administrators.setName(getTitle() + " Administrators"); + setAssociation(ADMIN_GROUP, administrators); + Group moderators = new Group(); moderators.setName(getTitle() + " Moderators"); setAssociation( MODERATION_GROUP, moderators ); @@ -176,6 +240,37 @@ // Actually, the only hack involved is making the email address unique. String email = "forum-moderator-" + getID() + "-" + moderators.getID() + "@" + s_config.getReplyHostName(); moderators.setPrimaryEmail(new EmailAddress(email)); + + // chr...@we... create additional groups for privilege + // assignment - could have assigned privileges directly without having associated + // groups, but this reduces rows in the (already enormous) dnm_permissions + // table + Group threadCreators = new Group(); + threadCreators.setName(getTitle() + " Thread Creators"); + setAssociation(THREAD_CREATE_GROUP, threadCreators); + + Group threadResponders = new Group(); + threadResponders.setName(getTitle() + " Thread Responders"); + setAssociation(THREAD_RESPONDER_GROUP, threadResponders); + + Group forumReaders = new Group(); + forumReaders.setName(getTitle() + " Readers"); + setAssociation(READ_GROUP, forumReaders); + + Group container = getGroup(); + + container.addSubgroup(administrators); + container.addSubgroup(moderators); + container.addSubgroup(threadCreators); + container.addSubgroup(threadResponders); + container.addSubgroup(forumReaders); + Group threadSubscriptions = new Group(); + threadSubscriptions.setName(THREAD_SUBSCRIPTION_GROUPS_NAME); + container.addSubgroup(threadSubscriptions); + container.save(); + + + } public void initialize() { @@ -184,8 +279,13 @@ if (isNew()) { setModerated(false); setNoticeboard(false); + setAllowFileAttachments(false); + setAllowImageUploads(false); + setAutoSubscribeThreadCreator(false); + setNoCategoryPostsAllowed(true); + setAnonymousPostsAllowed(false); createRootCategory(); - createModerationGroup(); + } } @@ -199,14 +299,71 @@ protected void afterSave() { if (m_wasNew) { PermissionService.setContext(getRootCategory(), this); + createGroups(); + if (getAdminGroup() != null) { + PermissionService.grantPermission( + new PermissionDescriptor( + PrivilegeDescriptor.ADMIN, + this, + getAdminGroup())); + s_log.debug( + "Current user : " + + Kernel.getContext().getParty().getPrimaryEmail() + + " class is " + + Kernel.getContext().getParty().getClass()); + // + // chr...@we... Original plan was that creator of forum + // is administrator by default, but party from Kernel at this point in code is + // acs-system-party - creation must happen in a KernelExcersion somewhere + // though I can't immediately see where. + // as a consequence, code below justs causes a classcast exception, + // + // revisit, but in meantime, only site admin can administer new forum + // until forum admin permissions set in UI + // + // User creator = (User) Kernel.getContext().getParty(); + // can't be null but let's be supercautious + // if (creator != null) { + // getAdminGroup().addMember(creator); + // } + /// + } if (getModerationGroup() != null ) { PermissionService.grantPermission( new PermissionDescriptor( PrivilegeDescriptor.get(FORUM_MODERATION_PRIVILEGE), this, - getModerationGroup()) - ); + getModerationGroup())); + } + if (getThreadCreateGroup() != null) { + PermissionService.grantPermission( + new PermissionDescriptor( + PrivilegeDescriptor.get(CREATE_THREAD_PRIVILEGE), + this, + getThreadCreateGroup())); + // chr...@we... + // wouldn't do this normally, but this enables legacy implementations + // to use new version without any side effects + // public can view forum by default and see create thread link - existing + // code forces login if link is selected + getThreadCreateGroup().addMember(Kernel.getPublicUser()); + } + + if (getThreadResponderGroup() != null) { + PermissionService.grantPermission( + new PermissionDescriptor( + PrivilegeDescriptor.get(RESPOND_TO_THREAD_PRIVILEGE), + this, + getThreadResponderGroup())); + } + + if (getReadGroup() != null) { + PermissionService.grantPermission( + new PermissionDescriptor( + PrivilegeDescriptor.READ, + this, + getReadGroup())); } KernelExcursion excursion = new KernelExcursion() { protected void excurse() { @@ -243,7 +400,15 @@ } - super.afterSave(); + // chr...@we... line removed. + // afterSave in Application sets permission + // context of forum to parent app (portal homepage) + // don't want to inherit permissions of portal, + // as public users have 'READ' privilege on this + // and so get shown postings in search results. + // + // + // super.afterSave(); } protected String getBaseDataObjectType() { @@ -287,6 +452,38 @@ } /** + * gets all pending messages and messages for reapproval - allows + * moderators to see which messages require their attention + * @return + */ + public DataAssociation getPendingPosts() { + // doesn't use getPosts in view of the warning that it + // may disappear + DataAssociation posts = (DataAssociation) get(POSTS); + FilterFactory factory = posts.getFilterFactory(); + Filter pending = factory.equals(Post.STATUS, Post.PENDING); + Filter reapprove = factory.equals(Post.STATUS, Post.REAPPROVE); + ; + + posts.addFilter(factory.or().addFilter(pending).addFilter(reapprove)); + + return posts; + } + + /** + * gets all suppressed messages - allows moderators to see which messages + * heve been rejectedrequire their attention + * @return + */ + public DataAssociation getSuppressedPosts() { + // doesn't use getPosts in view of the warning that it + // may disappear + DataAssociation posts = (DataAssociation) get(POSTS); + posts.addEqualsFilter(Post.STATUS, Post.SUPPRESSED); + return posts; + } + + /** * Gets a ThreadCollection of the threads in this forum. I.e. the * top-level posts which are not replies to any other post. */ @@ -358,9 +555,9 @@ } return threads; - } + } /** * Sets up instant and daily subscriptions for the forum. Daily @@ -527,6 +724,12 @@ return Boolean.TRUE.equals(get(NOTICEBOARD)); } + /** Returns the administrator group. Null if it doesn't exist */ + public Group getAdminGroup() { + DataObject dObj = (DataObject) get(ADMIN_GROUP); + Assert.exists(dObj, DataObject.class); + return (Group) DomainObjectFactory.newInstance(dObj); + } /** Returns the moderator group. Null if it doesn't exist */ public Group getModerationGroup() { DataObject dObj = (DataObject) get( MODERATION_GROUP ); @@ -534,6 +737,27 @@ return (Group)DomainObjectFactory.newInstance(dObj); } + /** Returns the thread create group. Null if it doesn't exist */ + public Group getThreadCreateGroup() { + DataObject dObj = (DataObject) get(THREAD_CREATE_GROUP); + Assert.exists(dObj, DataObject.class); + return (Group) DomainObjectFactory.newInstance(dObj); + } + + /** Returns the thread reply group. Null if it doesn't exist */ + public Group getThreadResponderGroup() { + DataObject dObj = (DataObject) get(THREAD_RESPONDER_GROUP); + Assert.exists(dObj, DataObject.class); + return (Group) DomainObjectFactory.newInstance(dObj); + } + + /** Returns the read group. Null if it doesn't exist */ + public Group getReadGroup() { + DataObject dObj = (DataObject) get(READ_GROUP); + Assert.exists(dObj, DataObject.class); + return (Group) DomainObjectFactory.newInstance(dObj); + } + public String getContextPath() { return "/ccm-forum"; } @@ -555,7 +779,7 @@ } LifecycleDefinition newLife = new LifecycleDefinition(); newLife.setLabel("Delete expired noticeboard postings"); - newLife.addPhaseDefinition("Noticeboard posting lifespan", + newLife.addPhaseDefinition("Forum posting lifespan", null, new Integer(0), new Integer(1440 * value), // in minutes @@ -565,13 +789,20 @@ // have the same expiration policy. DataAssociationCursor posts = getPosts().cursor(); while (posts.next()) { - Post post - = (Post)DomainObjectFactory.newInstance( - posts.getDataObject()); - s_log.debug("Resetting expiration lifecycle for " + post.getOID()); + Post post = + (Post) DomainObjectFactory.newInstance(posts.getDataObject()); + if (post + .getThread() + .getRootMessage() + .getID() + .equals(post.getID())) { + + s_log.debug( + "Resetting expiration lifecycle for " + post.getOID()); post.setLifecycle(newLife); } } + } public int getExpireAfter() { BigDecimal expire = (BigDecimal) get(EXPIRE_AFTER); @@ -594,5 +825,119 @@ set(LIFECYCLE_DEFINITION, life); } + /** + * method required for upgrade - normally groups are set during forum creation and so + * there is no need to invoke a setter + * @author cgyg9330 + * + */ + public void setAdminGroup(Group group) { + setAssociation(ADMIN_GROUP, group); + PermissionService.grantPermission( + new PermissionDescriptor(PrivilegeDescriptor.ADMIN, this, group)); + } + /** + * method required for upgrade - normally groups are set during forum creation and so + * there is no need to invoke a setter + * @author cgyg9330 + * + */ + public void setThreadCreatorGroup(Group group) { + setAssociation(THREAD_CREATE_GROUP, group); + PermissionService.grantPermission( + new PermissionDescriptor( + PrivilegeDescriptor.get(CREATE_THREAD_PRIVILEGE), + this, + group)); + } + /** + * method required for upgrade - normally groups are set during forum creation and so + * there is no need to invoke a setter + * @author cgyg9330 + * + */ + public void setThreadResponderGroup(Group group) { + setAssociation(THREAD_RESPONDER_GROUP, group); + PermissionService.grantPermission( + new PermissionDescriptor( + PrivilegeDescriptor.get(RESPOND_TO_THREAD_PRIVILEGE), + this, + group)); + } + /** + * method required for upgrade - normally groups are set during forum creation and so + * there is no need to invoke a setter + * @author cgyg9330 + * + */ + public void setReaderGroup(Group group) { + setAssociation(READ_GROUP, group); + PermissionService.grantPermission( + new PermissionDescriptor(PrivilegeDescriptor.READ, this, group)); + } + + /** + * @return + */ + public boolean allowFileAttachments() { + return ((Boolean) get(ALLOW_FILE_ATTACHMENTS)).booleanValue(); + } + public boolean allowImageUploads() { + return ((Boolean) get(ALLOW_IMAGE_UPLOADS)).booleanValue(); + } + + public boolean autoSubscribeThreadStarter() { + return ((Boolean) get(AUTOSUBSCRIBE_THREAD_STARTER)).booleanValue(); + } + + public boolean noCategoryPostsAllowed() { + return ((Boolean) get(NO_CATEGORY_POSTS)).booleanValue(); + } + public boolean anonymousPostsAllowed() { + return ((Boolean) get(ANONYMOUS_POSTS)).booleanValue(); + } + + public void setAllowFileAttachments(boolean allow) { + set(ALLOW_FILE_ATTACHMENTS, new Boolean(allow)); + } + public void setAllowImageUploads(boolean allow) { + set(ALLOW_IMAGE_UPLOADS, new Boolean(allow)); + } + public void setAutoSubscribeThreadCreator(boolean subscribe) { + set(AUTOSUBSCRIBE_THREAD_STARTER, new Boolean(subscribe)); + } + + public void setNoCategoryPostsAllowed(boolean allow) { + set(NO_CATEGORY_POSTS, new Boolean(allow)); + } + + public void setAnonymousPostsAllowed(boolean allow) { + set(ANONYMOUS_POSTS, new Boolean(allow)); + } + + public void setTitle (String title) { + String oldTitle = getTitle(); + super.setTitle(title); + if (!oldTitle.equals(title)) { + // 1. rename permission groups + getAdminGroup().setName(title + " Administrators"); + getModerationGroup().setName(title + " Moderators"); + getThreadCreateGroup().setName(title + " Thread Creators"); + getThreadResponderGroup().setName(title + " Thread Responders"); + getReadGroup().setName(title + " Readers"); + DataCollection subscriptions = getSubscriptions(); + while (subscriptions.next()) { + ForumSubscription subscription = (ForumSubscription)DomainObjectFactory.newInstance(subscriptions.getDataObject()); + subscription.getGroup().setName(subscription.getGroupName(this)); + } + ThreadCollection threads = getThreads(); + while (threads.next()) { + ThreadSubscription threadSub = ThreadSubscription.getThreadSubscription(threads.getMessageThread()); + threadSub.getGroup().setName(threadSub.getSubscriptionGroupName(this)); + + } + + } } +} Modified: aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java =================================================================== --- aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java 2007-09-17 09:00:10 UTC (rev 1628) @@ -18,33 +18,34 @@ */ package com.arsdigita.forum; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.log4j.Logger; + +import com.arsdigita.kernel.User; +import com.arsdigita.kernel.UserCollection; import com.arsdigita.runtime.AbstractConfig; +import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.parameter.BooleanParameter; +import com.arsdigita.util.parameter.IntegerParameter; import com.arsdigita.util.parameter.Parameter; -import com.arsdigita.util.parameter.URLParameter; import com.arsdigita.util.parameter.StringParameter; -import com.arsdigita.util.UncheckedWrapperException; - -import com.arsdigita.kernel.UserCollection; -import com.arsdigita.kernel.User; +import com.arsdigita.util.parameter.URLParameter; import com.arsdigita.web.Web; -import java.io.InputStream; -import java.io.IOException; -import java.net.URL; -import java.net.MalformedURLException; -import org.apache.log4j.Logger; - /** * A set of configuration parameters for forums. * * @author Justin Ross <jr...@re...> - * @version $Id$ + * @version $Id$ */ public class ForumConfig extends AbstractConfig { public final static String versionId = - "$Id$" + - "$Author$" + + "$Id$" + + "$Author$" + "$DateTime: 2004/08/17 23:26:27 $"; private static final Logger s_log = Logger.getLogger(ForumConfig.class); @@ -53,7 +54,18 @@ private Parameter m_authorEditPosts; private Parameter m_digestUserEmail; private Parameter m_replyHostName; + private Parameter m_disablePageCaching; + private Parameter m_adminOnlyCreateTopics; + private Parameter m_maxImageSize; + private Parameter m_maxFileSize; private final Parameter m_adapters; + private Parameter m_showAllThreadAlerts; + private Parameter m_showNewTabs; + private Parameter m_useWysiwygEditor; + private Parameter m_rejectionMessage; + private Parameter m_threadPageSize; + private Parameter m_quickFinish; + private Parameter m_deleteSentSubscriptionNotifications; public ForumConfig() { m_adminEditPosts = new BooleanParameter( @@ -72,6 +84,49 @@ "com.arsdigita.forum.digest_user_email", Parameter.OPTIONAL, null); + m_disablePageCaching = new BooleanParameter ( + "com.arsdigita.forum.disable_page_caching", + Parameter.REQUIRED, + Boolean.FALSE); + + m_adminOnlyCreateTopics = new BooleanParameter ( + "com.arsdigita.forum.admin_only_to_create_topics", + Parameter.REQUIRED, + Boolean.FALSE); + m_maxImageSize = new IntegerParameter ( + "com.arsdigita.forum.maximum_image_size", + Parameter.OPTIONAL, null); + m_maxFileSize = new IntegerParameter ( + "com.arsdigita.forum.maximum_file_size", + Parameter.OPTIONAL, null); + + m_showNewTabs = new BooleanParameter( + "com.arsdigita.forum.show_new_tabs", + Parameter.OPTIONAL, + Boolean.FALSE); + m_showAllThreadAlerts = new BooleanParameter( + "com.arsdigita.forum.show_all_forum_thread_alerts", + Parameter.OPTIONAL, + Boolean.TRUE); + m_useWysiwygEditor = new BooleanParameter( + "com.arsdigita.forum.use_wysiwyg_editor", + Parameter.OPTIONAL, + Boolean.FALSE); + m_rejectionMessage = new StringParameter( + "com.arsdigita.forum.rejection_form_message.example", + Parameter.OPTIONAL, + null); + m_threadPageSize = new IntegerParameter ( + "com.arsdigita.forum.thread_page_size", + Parameter.REQUIRED, new Integer(10)); + m_quickFinish = new BooleanParameter( + "com.arsdigita.forum.allow_quick_finish", + Parameter.OPTIONAL, + Boolean.FALSE); + m_deleteSentSubscriptionNotifications = new BooleanParameter( + "com.arsdigita.forum.delete_sent_subscription_notifications", + Parameter.OPTIONAL, + Boolean.FALSE); try { m_adapters = new URLParameter @@ -88,6 +143,17 @@ register(m_authorEditPosts); register(m_replyHostName); register(m_adapters); + register(m_disablePageCaching); + register(m_adminOnlyCreateTopics); + register(m_maxImageSize); + register(m_maxFileSize); + register(m_showAllThreadAlerts); + register(m_showNewTabs); + register(m_useWysiwygEditor); + register(m_rejectionMessage); + register(m_threadPageSize); + register(m_quickFinish); + register(m_deleteSentSubscriptionNotifications); loadInfo(); } @@ -123,6 +189,28 @@ return hostName; } + /** + * Supports prevention of client and middleware caching - + * use in situations where users with different + * permissions share machines + * @return + */ + public boolean disableClientPageCaching () { + return ((Boolean)get(m_disablePageCaching)).booleanValue(); + } + + /** + * if true, disables topic tab for non admin users. Topic + * tab does not access control topic creation, so set this + * to true to maintain control of the topics on the forum. + * + * + * + * @return + */ + public boolean topicCreationByAdminOnly () { + return ((Boolean)get(m_adminOnlyCreateTopics)).booleanValue(); + } public User getDigestUser() { String email = getDigestUserEmail(); @@ -138,4 +226,85 @@ users.close(); return user; } + /** + * returns the maximum allowed size (in bytes) of + * image files attached to posts. Any larger + * files are rejected by UI validation + * @return + */ + public long getMaxImageSize() { + Integer size = (Integer)get(m_maxImageSize); + long longSize = Long.MAX_VALUE; + if (size != null) { + longSize = size.longValue(); + } + return longSize; + } + /** + * returns the maximum allowed size (in bytes) of + * files attached to posts. Any larger + * files are rejected by UI validation + * @return + */ + public long getMaxFileSize() { + Integer size = (Integer)get(m_maxFileSize); + long longSize = Long.MAX_VALUE; + if (size != null) { + longSize = size.longValue(); + } + return longSize; + } + + + /** + * if true, alerts tab displays thread alerts for this and all other + * forums. If false, only display thread subscriptions for current + * forum. + * @return + */ + /* + * If true, the thread alert page lists thread alerts from + * all forums - alerts not from the current forum have + * links to the thread displayed within the context of + * the current forum. Looks weird and needs to be sorted out + * presumably the correct forum needs to be set in the ForumContext + * when a link is selected + */ + public boolean showThreadAlertsForAllForums () { + return ((Boolean)get(m_showAllThreadAlerts)).booleanValue(); + } + /** + * if true, displays setup and permissions tabs + * @return + */ + public boolean showNewTabs () { + return ((Boolean)get(m_showNewTabs)).booleanValue(); + } + + public boolean useWysiwygEditor () { + return ((Boolean)get(m_useWysiwygEditor)).booleanValue(); + } + + /** + * message added to the bottom of the moderation reection email. + * May give details about what the poster can do if not happy + * with rejection + * @return + */ + public String getRejectionMessage () { + return (String)get(m_rejectionMessage); + } + + public int getThreadPageSize () { + return ((Integer)get(m_threadPageSize)).intValue(); + } + + public boolean quickFinishAllowed () { + return ((Boolean)get(m_quickFinish)).booleanValue(); + } + + public boolean deleteNotifications () { + return ((Boolean)get(m_deleteSentSubscriptionNotifications)).booleanValue(); + } + } Modified: aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties =================================================================== --- aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties 2007-09-14 19:02:59 UTC (rev 1627) +++ aplaws/trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parame... [truncated message content] |
From: <cl...@fe...> - 2007-09-14 20:00:25
|
Author: clasohm Date: 2007-09-14 22:00:09 +0200 (Fri, 14 Sep 2007) New Revision: 1627 Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java Log: removed duplicate left hand side in variable assignment Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java 2007-09-14 07:24:16 UTC (rev 1626) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java 2007-09-14 20:00:09 UTC (rev 1627) @@ -81,7 +81,7 @@ if (category != null && category.hasChildCategories()) { CategoryCollection children = category.getChildren(); String order = ContentSection.getConfig().getCategoryTreeOrder(); - order = order = Category.SORT_KEY.equals(order) ? "link." + order : order; + order = Category.SORT_KEY.equals(order) ? "link." + order : order; children.addOrder(order); // children.addOrder("link." + Category.SORT_KEY); return new CategoryCollectionListModel(children); |
Author: clasohm Date: 2007-09-14 10:00:09 +0200 (Fri, 14 Sep 2007) New Revision: 1626 Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java Log: improved the SectionTarget data model Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl 2007-09-13 15:39:37 UTC (rev 1625) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl 2007-09-14 08:00:09 UTC (rev 1626) @@ -51,7 +51,7 @@ } -object type SectionOption extends ACSObject { +object type SectionOption extends ContentItem { String[1..1] label = cam_section_options.label; String[1..1] value = cam_section_options.value; @@ -63,17 +63,15 @@ association { // Association between a section and its options - composite TreeSection[1..1] section = + composite TreeSection[1..1] treeSection = join cam_section_options.section_id to cam_tree_sections.section_id; component SectionOption[0..n] sectionOptions = join cam_tree_sections.section_id to cam_section_options.section_id; } -object type SectionTarget extends ACSObject { +object type SectionTarget extends ContentItem { - SectionOption[1..1] matchOption = - join cam_section_targets.match_option to cam_section_options.option_id; String[0..1] targetURL = cam_section_targets.target_url; TreeSection[0..1] targetSection = join cam_section_targets.target_section to cam_tree_sections.section_id; @@ -83,11 +81,11 @@ } association { - // Association between a section and its targets + // Association between an option and its targets - TreeSection[1..1] section = - join cam_section_targets.section_id to cam_tree_sections.section_id; - component SectionTarget[0..n] sectionTargets = - join cam_tree_sections.section_id to cam_section_targets.section_id; + composite SectionOption[1..1] matchOption = + join cam_section_targets.match_option to cam_section_options.option_id; + component SectionTarget[0..n] sectionOptions = + join cam_section_options.option_id to cam_section_targets.match_option; } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java 2007-09-13 15:39:37 UTC (rev 1625) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java 2007-09-14 08:00:09 UTC (rev 1626) @@ -126,8 +126,8 @@ DataCollection collection = ssn.retrieve(SectionOption.BASE_DATA_OBJECT_TYPE); SectionOptionCollection options = new SectionOptionCollection(collection); - options.addEqualsFilter("section.tree.id", getID()); - options.addOrder("section.title, label"); + options.addEqualsFilter("treeSection.tree.id", getID()); + options.addOrder("treeSection.title, label"); return options; } @@ -137,8 +137,8 @@ DataCollection collection = ssn.retrieve(SectionTarget.BASE_DATA_OBJECT_TYPE); SectionTargetCollection options = new SectionTargetCollection(collection); - options.addEqualsFilter("section.tree.id", getID()); - options.addOrder("section.title, matchOption.label"); + options.addEqualsFilter("matchOption.treeSection.tree.id", getID()); + options.addOrder("matchOption.treeSection.title, matchOption.label"); return options; } } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java 2007-09-13 15:39:37 UTC (rev 1625) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java 2007-09-14 08:00:09 UTC (rev 1626) @@ -19,16 +19,13 @@ package com.arsdigita.camden.cms.contenttypes; import java.math.BigDecimal; -import java.sql.SQLException; import org.apache.log4j.Logger; -import com.arsdigita.db.Sequences; +import com.arsdigita.cms.ContentItem; import com.arsdigita.domain.DataObjectNotFoundException; -import com.arsdigita.kernel.ACSObject; import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; -import com.arsdigita.util.UncheckedWrapperException; /** * A section option of the Camden Decision Tree content type. @@ -36,7 +33,7 @@ * @author Carsten Clasohm * @version $Id$ */ -public class SectionOption extends ACSObject { +public class SectionOption extends ContentItem { private static final Logger s_log = Logger.getLogger(SectionOption.class); public static final String versionId = "$Id$"; @@ -44,7 +41,7 @@ public static final String BASE_DATA_OBJECT_TYPE = "com.arsdigita.camden.cms.contenttypes.SectionOption"; - public static final String SECTION = "section"; + public static final String TREE_SECTION = "treeSection"; public static final String LABEL = "label"; public static final String VALUE = "value"; @@ -77,13 +74,13 @@ } public TreeSection getSection() { - DataObject dataObject = (DataObject) get(SECTION); + DataObject dataObject = (DataObject) get(TREE_SECTION); if (dataObject == null) { return null; } return new TreeSection(dataObject); } public void setSection(TreeSection value) { - setAssociation(SECTION, value); + setAssociation(TREE_SECTION, value); } public String getLabel() { Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java 2007-09-13 15:39:37 UTC (rev 1625) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java 2007-09-14 08:00:09 UTC (rev 1626) @@ -19,14 +19,11 @@ package com.arsdigita.camden.cms.contenttypes; import java.math.BigDecimal; -import java.sql.SQLException; -import com.arsdigita.db.Sequences; +import com.arsdigita.cms.ContentItem; import com.arsdigita.domain.DataObjectNotFoundException; -import com.arsdigita.kernel.ACSObject; import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; -import com.arsdigita.util.UncheckedWrapperException; /** * A section target of the Camden Decision Tree content type. @@ -34,14 +31,13 @@ * @author Carsten Clasohm * @version $Id$ */ -public class SectionTarget extends ACSObject { +public class SectionTarget extends ContentItem { public static final String versionId = "$Id$"; public static final String BASE_DATA_OBJECT_TYPE = "com.arsdigita.camden.cms.contenttypes.SectionTarget"; - public static final String SECTION = "section"; public static final String MATCH_OPTION = "matchOption"; public static final String TARGET_URL = "targetURL"; public static final String TARGET_SECTION = "targetSection"; @@ -74,16 +70,6 @@ return BASE_DATA_OBJECT_TYPE; } - public TreeSection getSection() { - DataObject dataObject = (DataObject) get(SECTION); - if (dataObject == null) { return null; } - return new TreeSection(dataObject); - } - - public void setSection(TreeSection value) { - set(SECTION, value); - } - public SectionOption getMatchOption() { DataObject dataObject = (DataObject) get(MATCH_OPTION); if (dataObject == null) { return null; } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java 2007-09-13 15:39:37 UTC (rev 1625) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java 2007-09-14 08:00:09 UTC (rev 1626) @@ -250,11 +250,15 @@ option = new SectionOption(id); } else { option = new SectionOption(); + option.setName("SectionOption " + option.getID()); } + + String label = (String)data.get(LABEL); + String value = (String)data.get(VALUE); option.setSection(section); - option.setLabel((String)data.get(LABEL)); - option.setValue((String)data.get(VALUE)); + option.setLabel(label); + option.setValue(value); if (m_container != null) { m_container.onlyShowComponent( Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java 2007-09-13 15:39:37 UTC (rev 1625) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java 2007-09-14 08:00:09 UTC (rev 1626) @@ -73,7 +73,6 @@ private SingleSelect m_targetSectionWidget; private TextField m_targetURLWidget; - public static final String SECTION = "section"; public static final String MATCH_OPTION = "matchOption"; public static final String TARGET_URL = "targetURL"; public static final String TARGET_SECTION = "targetSection"; @@ -192,7 +191,8 @@ if (sectionOptions != null) { while (sectionOptions.next()) { SectionOption sectionOption = sectionOptions.getOption(); - Option option = new Option(sectionOption.getID().toString(), sectionOption.getLabel()); + String label = sectionOption.getSection().getTitle() + " : " + sectionOption.getLabel(); + Option option = new Option(sectionOption.getID().toString(), label); target.addOption(option, state); } } @@ -210,7 +210,6 @@ // retrieve the selected SectionTarget from the persistence layer SectionTarget target = new SectionTarget(id); - data.put(SECTION, target.getSection().getID()); data.put(MATCH_OPTION, target.getMatchOption().getID()); data.put(TARGET_URL, target.getTargetURL()); @@ -227,23 +226,6 @@ Option pleaseSelect = new Option("", (String)DecisionTreeUtil.globalize("form.please_select").localize()); Option none = new Option("", (String)DecisionTreeUtil.globalize("form.none").localize()); - add(new Label(DecisionTreeUtil.globalize("form_label.section"))); - m_sectionWidget = new SingleSelect(SECTION); - m_sectionWidget.addValidationListener(new NotNullValidationListener()); - m_sectionWidget.addOption(pleaseSelect); - - try { - m_sectionWidget.addPrintListener(new PrintListener() { - public void prepare(PrintEvent e) { - initSectionOptions(e); - } - }); - } catch (TooManyListenersException e) { - throw new RuntimeException(e); - } - - add(m_sectionWidget); - add(new Label(DecisionTreeUtil.globalize("form_label.match_value"))); m_matchValueWidget = new SingleSelect(MATCH_OPTION); m_matchValueWidget.addValidationListener(new NotNullValidationListener()); @@ -327,7 +309,6 @@ PageState state = event.getPageState(); FormData data = event.getFormData(); - TreeSection section = new TreeSection(new BigDecimal((String)data.get(SECTION))); SectionOption matchOption = new SectionOption(new BigDecimal((String)data.get(MATCH_OPTION))); TreeSection targetSection = null; @@ -343,9 +324,9 @@ target = new SectionTarget(id); } else { target = new SectionTarget(); + target.setName("SectionTarget " + target.getID()); } - target.setSection(section); target.setMatchOption(matchOption); target.setTargetURL((String)data.get(TARGET_URL)); target.setTargetSection(targetSection); Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java 2007-09-13 15:39:37 UTC (rev 1625) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java 2007-09-14 08:00:09 UTC (rev 1626) @@ -144,7 +144,7 @@ String colName = (String) col.getHeaderValue(); if (COL_SECTION.equals(colName)) { - return m_target.getSection().getTitle(); + return m_target.getMatchOption().getSection().getTitle(); } else if (COL_MATCH.equals(colName)) { return m_target.getMatchOption().getLabel(); } else if (COL_EDIT.equals(colName)) { |
From: <cl...@fe...> - 2007-09-13 16:00:18
|
Author: clasohm Date: 2007-09-13 18:00:10 +0200 (Thu, 13 Sep 2007) New Revision: 1625 Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeInitializer.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java Log: fixed publication Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl 2007-09-13 13:51:21 UTC (rev 1624) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl 2007-09-13 16:00:10 UTC (rev 1625) @@ -19,6 +19,7 @@ model com.arsdigita.camden.cms.contenttypes; +import com.arsdigita.kernel.ACSObject; import com.arsdigita.cms.*; object type DecisionTree extends ContentPage { @@ -50,13 +51,12 @@ } -object type SectionOption { +object type SectionOption extends ACSObject { - BigDecimal[1..1] id = cam_section_options.option_id; String[1..1] label = cam_section_options.label; String[1..1] value = cam_section_options.value; - object key (id); + reference key (cam_section_options.option_id); } @@ -70,16 +70,15 @@ } -object type SectionTarget { +object type SectionTarget extends ACSObject { - BigDecimal[1..1] id = cam_section_targets.target_id; SectionOption[1..1] matchOption = join cam_section_targets.match_option to cam_section_options.option_id; String[0..1] targetURL = cam_section_targets.target_url; TreeSection[0..1] targetSection = join cam_section_targets.target_section to cam_tree_sections.section_id; - object key (id); + reference key (cam_section_targets.target_id); } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeInitializer.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeInitializer.java 2007-09-13 13:51:21 UTC (rev 1624) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeInitializer.java 2007-09-13 16:00:10 UTC (rev 1625) @@ -18,10 +18,13 @@ */ package com.arsdigita.camden.cms.contenttypes; -import org.apache.log4j.Logger; - import com.arsdigita.cms.contenttypes.ContentTypeInitializer; import com.arsdigita.cms.search.ContentPageMetadataProvider; +import com.arsdigita.domain.DomainObject; +import com.arsdigita.domain.DomainObjectFactory; +import com.arsdigita.domain.DomainObjectInstantiator; +import com.arsdigita.persistence.DataObject; +import com.arsdigita.runtime.DomainInitEvent; import com.arsdigita.runtime.LegacyInitEvent; import com.arsdigita.search.MetadataProviderRegistry; @@ -43,6 +46,24 @@ new ContentPageMetadataProvider()); } + public void init(DomainInitEvent evt) { + super.init(evt); + + DomainObjectFactory f = evt.getFactory(); + + f.registerInstantiator(SectionOption.BASE_DATA_OBJECT_TYPE, new DomainObjectInstantiator() { + protected DomainObject doNewInstance(DataObject dataObject) { + return new SectionOption(dataObject); + } + }); + + f.registerInstantiator(SectionTarget.BASE_DATA_OBJECT_TYPE, new DomainObjectInstantiator() { + protected DomainObject doNewInstance(DataObject dataObject) { + return new SectionTarget(dataObject); + } + }); + } + public DecisionTreeInitializer() { super("ccm-ldn-camden-decisiontree.pdl.mf", DecisionTree.BASE_DATA_OBJECT_TYPE); } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java 2007-09-13 13:51:21 UTC (rev 1624) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionOption.java 2007-09-13 16:00:10 UTC (rev 1625) @@ -23,11 +23,9 @@ import org.apache.log4j.Logger; -import com.arsdigita.camden.cms.contenttypes.ui.OptionTable; import com.arsdigita.db.Sequences; import com.arsdigita.domain.DataObjectNotFoundException; -import com.arsdigita.domain.DomainObject; -import com.arsdigita.logging.Log; +import com.arsdigita.kernel.ACSObject; import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; import com.arsdigita.util.UncheckedWrapperException; @@ -38,7 +36,7 @@ * @author Carsten Clasohm * @version $Id$ */ -public class SectionOption extends DomainObject { +public class SectionOption extends ACSObject { private static final Logger s_log = Logger.getLogger(SectionOption.class); public static final String versionId = "$Id$"; @@ -46,7 +44,6 @@ public static final String BASE_DATA_OBJECT_TYPE = "com.arsdigita.camden.cms.contenttypes.SectionOption"; - public static final String ID = "id"; public static final String SECTION = "section"; public static final String LABEL = "label"; public static final String VALUE = "value"; @@ -79,22 +76,6 @@ return BASE_DATA_OBJECT_TYPE; } - public void initialize() { - super.initialize(); - - if (get(ID) == null) { - try { - set(ID, Sequences.getNextValue()); - } catch (SQLException ex) { - throw new UncheckedWrapperException("cannot assign id", ex); - } - } - } - - public BigDecimal getID() { - return (BigDecimal) get(ID); - } - public TreeSection getSection() { DataObject dataObject = (DataObject) get(SECTION); if (dataObject == null) { return null; } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java 2007-09-13 13:51:21 UTC (rev 1624) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java 2007-09-13 16:00:10 UTC (rev 1625) @@ -21,11 +21,9 @@ import java.math.BigDecimal; import java.sql.SQLException; -import org.apache.log4j.Logger; - import com.arsdigita.db.Sequences; import com.arsdigita.domain.DataObjectNotFoundException; -import com.arsdigita.domain.DomainObject; +import com.arsdigita.kernel.ACSObject; import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; import com.arsdigita.util.UncheckedWrapperException; @@ -36,14 +34,13 @@ * @author Carsten Clasohm * @version $Id$ */ -public class SectionTarget extends DomainObject { +public class SectionTarget extends ACSObject { public static final String versionId = "$Id$"; public static final String BASE_DATA_OBJECT_TYPE = "com.arsdigita.camden.cms.contenttypes.SectionTarget"; - public static final String ID = "id"; public static final String SECTION = "section"; public static final String MATCH_OPTION = "matchOption"; public static final String TARGET_URL = "targetURL"; @@ -77,22 +74,6 @@ return BASE_DATA_OBJECT_TYPE; } - public void initialize() { - super.initialize(); - - if (get(ID) == null) { - try { - set(ID, Sequences.getNextValue()); - } catch (SQLException ex) { - throw new UncheckedWrapperException("cannot assign id", ex); - } - } - } - - public BigDecimal getID() { - return (BigDecimal) get(ID); - } - public TreeSection getSection() { DataObject dataObject = (DataObject) get(SECTION); if (dataObject == null) { return null; } |
From: <cl...@fe...> - 2007-09-13 14:00:21
|
Author: clasohm Date: 2007-09-13 16:00:17 +0200 (Thu, 13 Sep 2007) New Revision: 1624 Added: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewTargets.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetDeleteForm.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/WEB-INF/content-types/com/arsdigita/camden/cms/contenttypes/DecisionTree.xml aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeResources.properties aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTargetCollection.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewOptions.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionDeleteForm.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionTable.java Log: worked on authoring UI for SectionTargets Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/pdl/com/arsdigita/camden/cms/contenttypes/DecisionTree.pdl 2007-09-13 14:00:17 UTC (rev 1624) @@ -72,20 +72,21 @@ object type SectionTarget { - BigDecimal[1..1] targetId = cam_section_targets.target_id; - String[1..1] matchValue = cam_section_targets.match_value; + BigDecimal[1..1] id = cam_section_targets.target_id; + SectionOption[1..1] matchOption = + join cam_section_targets.match_option to cam_section_options.option_id; String[0..1] targetURL = cam_section_targets.target_url; TreeSection[0..1] targetSection = - join cam_section_targets.section_id to cam_tree_sections.section_id; + join cam_section_targets.target_section to cam_tree_sections.section_id; - object key (targetId); + object key (id); } association { // Association between a section and its targets - TreeSection[1..1] sections = + TreeSection[1..1] section = join cam_section_targets.section_id to cam_tree_sections.section_id; component SectionTarget[0..n] sectionTargets = join cam_tree_sections.section_id to cam_section_targets.section_id; Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/WEB-INF/content-types/com/arsdigita/camden/cms/contenttypes/DecisionTree.xml =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/WEB-INF/content-types/com/arsdigita/camden/cms/contenttypes/DecisionTree.xml 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/WEB-INF/content-types/com/arsdigita/camden/cms/contenttypes/DecisionTree.xml 2007-09-13 14:00:17 UTC (rev 1624) @@ -23,7 +23,7 @@ descriptionBundle="com.arsdigita.camden.cms.contenttypes.DecisionTreeResources" component="com.arsdigita.camden.cms.contenttypes.ui.DecisionTreeViewOptions"/> - <ctd:include href="/WEB-INF/content-types/assign-categories-step.xml"/> + <ctd:authoring-step labelKey="section_targets.title" labelBundle="com.arsdigita.camden.cms.contenttypes.DecisionTreeResources" descriptionKey="section_targets.description" descriptionBundle="com.arsdigita.camden.cms.contenttypes.DecisionTreeResources" component="com.arsdigita.camden.cms.contenttypes.ui.DecisionTreeViewTargets" /><ctd:include href="/WEB-INF/content-types/assign-categories-step.xml"/> </ctd:authoring-kit> </ctd:content-type> </ctd:content-types> Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTree.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -130,4 +130,15 @@ options.addOrder("section.title, label"); return options; } + + + public SectionTargetCollection getTargets() { + Session ssn = SessionManager.getSession(); + DataCollection collection = ssn.retrieve(SectionTarget.BASE_DATA_OBJECT_TYPE); + + SectionTargetCollection options = new SectionTargetCollection(collection); + options.addEqualsFilter("section.tree.id", getID()); + options.addOrder("section.title, matchOption.label"); + return options; + } } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeResources.properties =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeResources.properties 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/DecisionTreeResources.properties 2007-09-13 14:00:17 UTC (rev 1624) @@ -1,3 +1,5 @@ +form.none=-- none -- +form.please_select=-- please select -- form_label.cancel_url=Cancel URL form_label.description=Description form_label.instructions=Instructions @@ -2,5 +4,10 @@ form_label.label=Label +form_label.match_value=Value to Match form_label.section=Section +form_label.target_section=Target Section +form_label.target_url=Target URL form_label.title=Title form_label.value=Value +form_validation.target_required=Either a target URL or a target section is required. +form_validation.duplicate_target=Please enter a target URL or a target section, but not both. properties.cancel_url=Cancel URL: @@ -14,6 +21,14 @@ section_options.no_options_yet=No options yet section_options.title=Section Options section_options.view_all_options=View all options +section_targets.add_new_target=Add new target +section_targets.add_target=Add Target +section_targets.delete_target=Delete target +section_targets.description=The targets that belong to this decision tree's sections. +section_targets.edit_target=Edit Target +section_targets.no_targets_yet=No targets yet +section_targets.title=Section Targets +section_targets.view_all_targets=View all targets tree_section.submission_cancelled=Submission cancelled tree_sections.add_new_section=Add new section tree_sections.add_section=Add Section Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTarget.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -19,13 +19,16 @@ package com.arsdigita.camden.cms.contenttypes; import java.math.BigDecimal; +import java.sql.SQLException; import org.apache.log4j.Logger; +import com.arsdigita.db.Sequences; import com.arsdigita.domain.DataObjectNotFoundException; import com.arsdigita.domain.DomainObject; import com.arsdigita.persistence.DataObject; import com.arsdigita.persistence.OID; +import com.arsdigita.util.UncheckedWrapperException; /** * A section target of the Camden Decision Tree content type. @@ -33,16 +36,18 @@ * @author Carsten Clasohm * @version $Id$ */ -class SectionTarget extends DomainObject { +public class SectionTarget extends DomainObject { public static final String versionId = "$Id$"; public static final String BASE_DATA_OBJECT_TYPE = "com.arsdigita.camden.cms.contenttypes.SectionTarget"; - public static final String MATCH_VALUE = "matchValue"; - public static final String TARGET_URL = "targetURL"; - public static final String TARGET_SECTION = "targetSection"; + public static final String ID = "id"; + public static final String SECTION = "section"; + public static final String MATCH_OPTION = "matchOption"; + public static final String TARGET_URL = "targetURL"; + public static final String TARGET_SECTION = "targetSection"; public SectionTarget() { this(BASE_DATA_OBJECT_TYPE); @@ -72,14 +77,42 @@ return BASE_DATA_OBJECT_TYPE; } - public String getMatchValue() { - return (String) get(MATCH_VALUE); + public void initialize() { + super.initialize(); + + if (get(ID) == null) { + try { + set(ID, Sequences.getNextValue()); + } catch (SQLException ex) { + throw new UncheckedWrapperException("cannot assign id", ex); + } + } } - public void setMatchValue(String value) { - set(MATCH_VALUE, value); + public BigDecimal getID() { + return (BigDecimal) get(ID); } + public TreeSection getSection() { + DataObject dataObject = (DataObject) get(SECTION); + if (dataObject == null) { return null; } + return new TreeSection(dataObject); + } + + public void setSection(TreeSection value) { + set(SECTION, value); + } + + public SectionOption getMatchOption() { + DataObject dataObject = (DataObject) get(MATCH_OPTION); + if (dataObject == null) { return null; } + return new SectionOption(dataObject); + } + + public void setMatchOption(SectionOption value) { + setAssociation(MATCH_OPTION, value); + } + public String getTargetURL() { return (String) get(TARGET_URL); } @@ -89,7 +122,9 @@ } public TreeSection getTargetSection() { - return (TreeSection) get(TARGET_SECTION); + DataObject dataObject = (DataObject) get(TARGET_SECTION); + if (dataObject == null) { return null; } + return new TreeSection(dataObject); } public void setTargetSection(TreeSection value) { Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTargetCollection.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTargetCollection.java 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/SectionTargetCollection.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -65,7 +65,7 @@ * the collection. * **/ - public SectionTarget getSectionTarget() { + public SectionTarget getTarget() { return (SectionTarget) getDomainObject(); } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewOptions.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewOptions.java 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewOptions.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -87,16 +87,16 @@ Assert.exists(m_selTree, ItemSelectionModel.class); // create the components and set default visibility - add(buildSectionTable(), true); - add(buildSectionEdit(), false); - add(buildSectionDelete(), false); + add(buildOptionTable(), true); + add(buildOptionEdit(), false); + add(buildOptionDelete(), false); } /** * Builds a container to hold a SectionTable and a link * to add a new section. */ - protected Container buildSectionTable () { + protected Container buildOptionTable () { ColumnPanel c = new ColumnPanel(1); c.setKey(OPTION_TABLE+m_typeIDStr); c.setBorderColor("#FFFFFF"); @@ -150,7 +150,7 @@ * Builds a container to hold a SectionEditForm and a link * to return to the section list. */ - protected Container buildSectionEdit () { + protected Container buildOptionEdit () { ColumnPanel c = new ColumnPanel(1); c.setKey(OPTION_EDIT+m_typeIDStr); c.setBorderColor("#FFFFFF"); @@ -185,7 +185,7 @@ * Builds a container to hold the component to confirm * deletion of a section. */ - protected Container buildSectionDelete () { + protected Container buildOptionDelete () { ColumnPanel c = new ColumnPanel(1); c.setKey(OPTION_DEL+m_typeIDStr); c.setBorderColor("#FFFFFF"); Added: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewTargets.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewTargets.java (rev 0) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/DecisionTreeViewTargets.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2007 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.camden.cms.contenttypes.ui; + + +import org.apache.log4j.Logger; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.Container; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +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.event.TableActionEvent; +import com.arsdigita.bebop.event.TableActionListener; +import com.arsdigita.bebop.table.TableColumn; +import com.arsdigita.camden.cms.contenttypes.DecisionTree; +import com.arsdigita.camden.cms.contenttypes.DecisionTreeUtil; +import com.arsdigita.camden.cms.contenttypes.TreeSection; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.SecurityManager; +import com.arsdigita.cms.contenttypes.ui.ResettableContainer; +import com.arsdigita.cms.dispatcher.Utilities; +import com.arsdigita.cms.ui.authoring.AuthoringKitWizard; +import com.arsdigita.util.Assert; + + +/** + * Authoring kit step to manage the targets for a DecisionTree. + * The editing process is implemented with three main visual components + * that manipulate the currently selected DecisionTree and targets. + * The visibility of these components is managed by this class. + * + * @author Carsten Clasohm + * @version $Id$ + */ +public class DecisionTreeViewTargets extends ResettableContainer +{ + /** id keys for each editing panel */ + public static final String TARGET_TABLE = "tgt_tbl"; + public static final String TARGET_EDIT = "tgt_edt"; + public static final String TARGET_DEL = "tgt_del"; + + /** class attributes */ + public static final String DATA_TABLE = "dataTable"; + public static final String ACTION_LINK = "actionLink"; + + protected AuthoringKitWizard m_wizard; + protected ItemSelectionModel m_selTree; + protected ItemSelectionModel m_selTarget; + + /** visual components that do the 'real work' */ + protected TargetTable m_targetTable; + protected TargetEditForm m_targetEdit; + protected TargetDeleteForm m_targetDelete; + + private String m_typeIDStr; + + public DecisionTreeViewTargets (ItemSelectionModel selTree, AuthoringKitWizard wizard) { + super(); + m_selTree = selTree; + m_wizard = wizard; + m_typeIDStr = wizard.getContentType().getID().toString(); + Assert.exists(m_selTree, ItemSelectionModel.class); + + // create the components and set default visibility + add(buildTargetTable(), true); + add(buildTargetEdit(), false); + add(buildTargetDelete(), false); + } + + /** + * Builds a container to hold a TargetTable and a link + * to add a new target. + */ + protected Container buildTargetTable () { + ColumnPanel c = new ColumnPanel(1); + c.setKey(TARGET_TABLE+m_typeIDStr); + c.setBorderColor("#FFFFFF"); + c.setPadColor("#FFFFFF"); + + m_targetTable = new TargetTable(m_selTree); + m_targetTable.setClassAttr(DATA_TABLE); + + // selected section is based on the selection in the SectionTable + m_selTarget = new ItemSelectionModel(TreeSection.class.getName(), + TreeSection.BASE_DATA_OBJECT_TYPE, + m_targetTable.getRowSelectionModel()); + + m_targetTable.setSectionModel(m_selTarget); + + Label emptyView = new Label(DecisionTreeUtil.globalize("section_targets.no_targets_yet")); + m_targetTable.setEmptyView(emptyView); + + // handle clicks to preview or delete a Section + m_targetTable.addTableActionListener (new TableActionListener () { + public void cellSelected (TableActionEvent event) { + PageState state = event.getPageState(); + + TableColumn col = m_targetTable.getColumnModel() + .get(event.getColumn().intValue()); + String colName = (String)col.getHeaderValue(); + + if (SectionTable.COL_DEL.equals(colName)) { + onlyShowComponent(state, TARGET_DEL+m_typeIDStr); + } else if (SectionTable.COL_EDIT.equals(colName)) { + onlyShowComponent(state, TARGET_EDIT+m_typeIDStr); + } else if (SectionTable.COL_FIRST.equals(colName)) { + DecisionTree tree = (DecisionTree)m_selTree.getSelectedObject(state); + TreeSection section = (TreeSection)m_selTarget.getSelectedItem(state); + tree.setFirstSection(section); + tree.save(); + } + } + + public void headSelected(TableActionEvent e) {} + }); + c.add(m_targetTable); + + // link to add new section + c.add(buildAddLink()); + + return c; + } + + /** + * Builds a container to hold a SectionEditForm and a link + * to return to the section list. + */ + protected Container buildTargetEdit () { + ColumnPanel c = new ColumnPanel(1); + c.setKey(TARGET_EDIT+m_typeIDStr); + c.setBorderColor("#FFFFFF"); + c.setPadColor("#FFFFFF"); + + // display an appropriate title + c.add( new Label( new PrintListener() { + public void prepare ( PrintEvent event ) { + PageState state = event.getPageState(); + Label label = (Label)event.getTarget(); + + if (m_selTarget.getSelectedKey(state) == null) { + label.setLabel((String)DecisionTreeUtil.globalize("section_targets.add_target").localize()); + } else { + label.setLabel((String)DecisionTreeUtil.globalize("section_targets.edit_target").localize()); + } + } + })); + + // form to edit a Section + m_targetEdit = new TargetEditForm(m_selTree, m_selTarget, this); + c.add(m_targetEdit); + + c.add(buildViewAllLink()); + // link to add new section + c.add(buildAddLink()); + + return c; + } + + /** + * Builds a container to hold the component to confirm + * deletion of a section. + */ + protected Container buildTargetDelete () { + ColumnPanel c = new ColumnPanel(1); + c.setKey(TARGET_DEL+m_typeIDStr); + c.setBorderColor("#FFFFFF"); + c.setPadColor("#FFFFFF"); + + c.add(new Label(DecisionTreeUtil.globalize("section_targets.delete_target"))); + m_targetDelete = new TargetDeleteForm(m_selTree, m_selTarget); + m_targetDelete.addSubmissionListener ( new FormSubmissionListener () { + public void submitted ( FormSectionEvent e ) { + PageState state = e.getPageState(); + onlyShowComponent(state, TARGET_TABLE+m_typeIDStr); + } + }); + c.add(m_targetDelete); + + c.add(buildViewAllLink()); + + return c; + } + + /** + * Utility method to create a link to display the section list. + */ + protected ActionLink buildViewAllLink () { + ActionLink viewAllLink = new ActionLink( (String) DecisionTreeUtil.globalize("section_targets.view_all_targets").localize()); + viewAllLink.setClassAttr(ACTION_LINK); + viewAllLink.addActionListener( new ActionListener() { + public void actionPerformed ( ActionEvent event ) { + onlyShowComponent(event.getPageState(), TARGET_TABLE+m_typeIDStr); + } + }); + + return viewAllLink; + } + + /** + * Utility method to create a link to display the section list. + */ + protected ActionLink buildAddLink () { + ActionLink addLink = new ActionLink( (String) DecisionTreeUtil.globalize("section_targets.add_new_target").localize()) { + public boolean isVisible(PageState state) { + SecurityManager sm = Utilities.getSecurityManager(state); + ContentItem item = (ContentItem)m_selTree.getSelectedObject(state); + + return super.isVisible(state) && + sm.canAccess(state.getRequest(), SecurityManager.EDIT_ITEM, + item); + + } + }; + addLink.setClassAttr(ACTION_LINK); + addLink.addActionListener( new ActionListener () { + public void actionPerformed ( ActionEvent event ) { + PageState state = event.getPageState(); + m_selTarget.clearSelection(state); + onlyShowComponent(state, TARGET_EDIT+m_typeIDStr); + } + }); + + return addLink; + } + + public String getTypeIDStr() { + return m_typeIDStr; + } +} Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionDeleteForm.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionDeleteForm.java 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionDeleteForm.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -111,7 +111,5 @@ SectionOption option = new SectionOption(id); option.delete(); - - s_log.info("option " + id + " delete"); } } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionEditForm.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -19,6 +19,7 @@ package com.arsdigita.camden.cms.contenttypes.ui; import java.math.BigDecimal; +import java.util.TooManyListenersException; import org.apache.log4j.Logger; @@ -34,6 +35,8 @@ import com.arsdigita.bebop.event.FormProcessListener; 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.Option; import com.arsdigita.bebop.form.SingleSelect; import com.arsdigita.bebop.form.TextField; @@ -47,6 +50,7 @@ import com.arsdigita.camden.cms.contenttypes.TreeSectionCollection; import com.arsdigita.cms.ItemSelectionModel; import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.util.Assert; /** * Form to edit a SectionOption for a DecisionTree. @@ -132,38 +136,41 @@ return m_saveCancelSection; } - /** Form initialisation hook. Sets the options for select widgets. + /** + * Set up the dynamic options for the section select widget. */ - public void init(FormSectionEvent fse) { - PageState state = fse.getPageState(); + private void initSectionOptions(PrintEvent e) { + PageState state = e.getPageState(); + SingleSelect target = (SingleSelect) e.getTarget(); DecisionTree tree = (DecisionTree)m_selTree.getSelectedObject(state); - FormData data = fse.getFormData(); if (tree != null) { TreeSectionCollection sections = tree.getSections(); - if (sections != null && !sections.isEmpty()) { + if (sections != null) { while (sections.next()) { TreeSection section = sections.getSection(); - Option option = - new Option(section.getID().toString(), section.getTitle()); - m_sectionWidget.addOption(option, state); + Option option = new Option(section.getID().toString(), section.getTitle()); + target.addOption(option, state); } } - } + } + } + + /** + * Form initialisation hook. + */ + public void init(FormSectionEvent fse) { + PageState state = fse.getPageState(); + FormData data = fse.getFormData(); + + if (m_selOption.getSelectedKey(state) != null) { + BigDecimal id = new BigDecimal(m_selOption.getSelectedKey(state).toString()); + // retrieve the selected Option from the persistence layer + SectionOption sectionOption = new SectionOption(id); - if (m_selOption.getSelectedKey(state) != null) { - BigDecimal id = new BigDecimal(m_selOption - .getSelectedKey(state).toString()); - try { - // retrieve the selected Option from the persistence layer - SectionOption option = new SectionOption(id); - - data.put(SECTION, option.getSection().getID()); - data.put(LABEL, option.getLabel()); - data.put(VALUE, option.getValue()); - } catch (DataObjectNotFoundException ex) { - s_log.error("Option(" + id + ") could not be found"); - } + data.put(SECTION, sectionOption.getSection().getID()); + data.put(LABEL, sectionOption.getLabel()); + data.put(VALUE, sectionOption.getValue()); } } @@ -171,10 +178,24 @@ * Add form widgets for a Section. */ protected void addWidgets() { - add(new Label(DecisionTreeUtil + Option pleaseSelect = new Option("", (String)DecisionTreeUtil.globalize("form.please_select").localize()); + + add(new Label(DecisionTreeUtil .globalize("form_label.section"))); m_sectionWidget = new SingleSelect(SECTION); m_sectionWidget.addValidationListener(new NotNullValidationListener()); + m_sectionWidget.addOption(pleaseSelect); + + try { + m_sectionWidget.addPrintListener(new PrintListener() { + public void prepare(PrintEvent e) { + initSectionOptions(e); + } + }); + } catch (TooManyListenersException e) { + throw new RuntimeException(e); + } + add(m_sectionWidget); add(new Label(DecisionTreeUtil @@ -225,12 +246,8 @@ if (m_selOption.getSelectedKey(state) != null) { BigDecimal id = new BigDecimal(m_selOption .getSelectedKey(state).toString()); - try { - // retrieve the selected Option from the persistence layer - option = new SectionOption(id); - } catch (DataObjectNotFoundException ex) { - s_log.error("Option(" + id + ") could not be found"); - } + // retrieve the selected Option from the persistence layer + option = new SectionOption(id); } else { option = new SectionOption(); } Modified: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionTable.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionTable.java 2007-09-13 13:06:35 UTC (rev 1623) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/OptionTable.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -43,7 +43,7 @@ /** - * A table that displays the sections for the currently + * A table that displays the options for the currently * selected DecisionTree. * * @author Carsten Clasohm Added: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetDeleteForm.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetDeleteForm.java (rev 0) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetDeleteForm.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2007 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.camden.cms.contenttypes.ui; + +import java.math.BigDecimal; + +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.SaveCancelSection; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.camden.cms.contenttypes.DecisionTreeUtil; +import com.arsdigita.camden.cms.contenttypes.SectionOption; +import com.arsdigita.camden.cms.contenttypes.SectionTarget; +import com.arsdigita.cms.ItemSelectionModel; + +/** + * A form to confirm deletion of a single section of a DecisionTree. + * + * @author Carsten Clasohm + * @version $Id$ + */ +public class TargetDeleteForm extends Form + implements FormInitListener, FormSubmissionListener, FormProcessListener +{ + private final static Logger s_log = Logger.getLogger(TargetDeleteForm.class.getName()); + + protected ItemSelectionModel m_selTree; + protected ItemSelectionModel m_selTarget; + protected SaveCancelSection m_saveCancelSection; + private Label m_targetMatchValue; + + public TargetDeleteForm (ItemSelectionModel selTree, ItemSelectionModel selTarget) { + super("TargetDeleteForm", new ColumnPanel(2)); + m_selTree = selTree; + m_selTarget = selTarget; + + ColumnPanel panel = (ColumnPanel)getPanel(); + panel.setBorder(false); + panel.setPadColor("#FFFFFF"); + panel.setColumnWidth(1, "20%"); + panel.setColumnWidth(2, "80%"); + panel.setWidth("100%"); + + m_targetMatchValue = new Label("Target Match Value"); + add(m_targetMatchValue, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + addSaveCancelSection(); + + addInitListener(this); + addSubmissionListener(this); + addProcessListener(this); + } + + protected SaveCancelSection addSaveCancelSection () { + m_saveCancelSection = new SaveCancelSection(); + m_saveCancelSection.getSaveButton().setButtonLabel("Delete"); + add(m_saveCancelSection, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + return m_saveCancelSection; + } + + public void init ( FormSectionEvent event ) throws FormProcessException { + PageState state = event.getPageState(); + + BigDecimal id = new BigDecimal(m_selTarget + .getSelectedKey(state).toString()); + SectionTarget target = new SectionTarget(id); + + m_targetMatchValue.setLabel(target.getMatchOption().getLabel(), state); + } + + public void submitted ( FormSectionEvent event ) throws FormProcessException { + PageState state = event.getPageState(); + + if ( m_saveCancelSection.getCancelButton().isSelected(state) ) { + throw new FormProcessException( (String) DecisionTreeUtil.globalize("tree_section.submission_cancelled").localize()); + } + } + + public void process ( FormSectionEvent event ) throws FormProcessException { + PageState state = event.getPageState(); + + BigDecimal id = new BigDecimal(m_selTarget + .getSelectedKey(state).toString()); + SectionTarget target = new SectionTarget(id); + + target.delete(); + } +} Added: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java (rev 0) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetEditForm.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2007 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.camden.cms.contenttypes.ui; + +import java.math.BigDecimal; +import java.util.TooManyListenersException; + +import org.apache.log4j.Logger; + +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormData; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SaveCancelSection; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.bebop.event.FormValidationListener; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.SingleSelect; +import com.arsdigita.bebop.form.TextField; +import com.arsdigita.bebop.parameters.NotNullValidationListener; +import com.arsdigita.camden.cms.contenttypes.DecisionTree; +import com.arsdigita.camden.cms.contenttypes.DecisionTreeUtil; +import com.arsdigita.camden.cms.contenttypes.SectionOption; +import com.arsdigita.camden.cms.contenttypes.SectionOptionCollection; +import com.arsdigita.camden.cms.contenttypes.SectionTarget; +import com.arsdigita.camden.cms.contenttypes.TreeSection; +import com.arsdigita.camden.cms.contenttypes.TreeSectionCollection; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.domain.DataObjectNotFoundException; + +/** + * Form to edit a SectionTarget for a DecisionTree. + * + * @author Carsten Clasohm + * @version $Id$ + */ +public class TargetEditForm extends Form +implements FormInitListener, FormProcessListener, FormSubmissionListener { + private final static Logger s_log = Logger.getLogger(TargetEditForm.class); + + private ItemSelectionModel m_selTree; + private ItemSelectionModel m_selTarget; + + private DecisionTreeViewTargets m_container; + + private SaveCancelSection m_saveCancelSection; + private SingleSelect m_sectionWidget; + private SingleSelect m_matchValueWidget; + private SingleSelect m_targetSectionWidget; + private TextField m_targetURLWidget; + + public static final String SECTION = "section"; + public static final String MATCH_OPTION = "matchOption"; + public static final String TARGET_URL = "targetURL"; + public static final String TARGET_SECTION = "targetSection"; + /** + * Constructor. + * + * @param selTree the current article + * @param selTarget the current section + */ + public TargetEditForm(ItemSelectionModel selTree, + ItemSelectionModel selTarget) { + this(selTree, selTarget, null); + } + + /** + * Constructor. + * + * @param selArticle the current article + * @param selTarget the current section + * @param container container which this form is added to + */ + public TargetEditForm(ItemSelectionModel selTree, + ItemSelectionModel selTarget, + DecisionTreeViewTargets container) { + super("TargetEditForm", new ColumnPanel(2)); + m_selTree = selTree; + m_selTarget = selTarget; + m_container = container; + + setMethod(Form.POST); + setEncType("multipart/form-data"); + + ColumnPanel panel = (ColumnPanel)getPanel(); + panel.setBorder(false); + panel.setPadColor("#FFFFFF"); + panel.setColumnWidth(1, "20%"); + panel.setColumnWidth(2, "80%"); + panel.setWidth("100%"); + + addWidgets(); + addSaveCancelSection(); + + addInitListener(this); + addSubmissionListener(this); + addProcessListener(this); + } + + /** + * Instantiate and add a save/cancel section to the form. + * + * @return the SaveCancelSection that was added + */ + protected SaveCancelSection addSaveCancelSection() { + m_saveCancelSection = new SaveCancelSection(); + add(m_saveCancelSection, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT); + return m_saveCancelSection; + } + + /** + * Returns the save/cancel section from this form. + */ + public SaveCancelSection getSaveCancelSection() { + return m_saveCancelSection; + } + + /** + * Set up the dynamic options for the section select widget. + */ + private void initSectionOptions(PrintEvent e) { + PageState state = e.getPageState(); + SingleSelect target = (SingleSelect) e.getTarget(); + DecisionTree tree = (DecisionTree)m_selTree.getSelectedObject(state); + + if (tree != null) { + TreeSectionCollection sections = tree.getSections(); + if (sections != null) { + while (sections.next()) { + TreeSection section = sections.getSection(); + Option option = new Option(section.getID().toString(), section.getTitle()); + target.addOption(option, state); + } + } + } + } + + /** + * Set up the dynamic options for the target select widget. + */ + private void initTargetOptions(PrintEvent e) { + PageState state = e.getPageState(); + SingleSelect target = (SingleSelect) e.getTarget(); + DecisionTree tree = (DecisionTree)m_selTree.getSelectedObject(state); + + if (tree != null) { + TreeSectionCollection sections = tree.getSections(); + if (sections != null) { + while (sections.next()) { + TreeSection section = sections.getSection(); + Option option = new Option(section.getID().toString(), section.getTitle()); + target.addOption(option, state); + } + } + } + } + + /** + * Set up the dynamic options for the match value select widget. + */ + private void initMatchOptions(PrintEvent e) { + PageState state = e.getPageState(); + SingleSelect target = (SingleSelect) e.getTarget(); + DecisionTree tree = (DecisionTree)m_selTree.getSelectedObject(state); + + if (tree != null) { + SectionOptionCollection sectionOptions = tree.getOptions(); + if (sectionOptions != null) { + while (sectionOptions.next()) { + SectionOption sectionOption = sectionOptions.getOption(); + Option option = new Option(sectionOption.getID().toString(), sectionOption.getLabel()); + target.addOption(option, state); + } + } + } + } + + /** Form initialisation hook. Sets the options for select widgets. + */ + public void init(FormSectionEvent fse) { + PageState state = fse.getPageState(); + FormData data = fse.getFormData(); + + if (m_selTarget.getSelectedKey(state) != null) { + BigDecimal id = new BigDecimal(m_selTarget.getSelectedKey(state).toString()); + // retrieve the selected SectionTarget from the persistence layer + SectionTarget target = new SectionTarget(id); + + data.put(SECTION, target.getSection().getID()); + data.put(MATCH_OPTION, target.getMatchOption().getID()); + data.put(TARGET_URL, target.getTargetURL()); + + TreeSection targetSection = target.getTargetSection(); + if (targetSection != null) + data.put(TARGET_SECTION, targetSection.getID()); + } + } + + /** + * Add form widgets for a Section. + */ + protected void addWidgets() { + Option pleaseSelect = new Option("", (String)DecisionTreeUtil.globalize("form.please_select").localize()); + Option none = new Option("", (String)DecisionTreeUtil.globalize("form.none").localize()); + + add(new Label(DecisionTreeUtil.globalize("form_label.section"))); + m_sectionWidget = new SingleSelect(SECTION); + m_sectionWidget.addValidationListener(new NotNullValidationListener()); + m_sectionWidget.addOption(pleaseSelect); + + try { + m_sectionWidget.addPrintListener(new PrintListener() { + public void prepare(PrintEvent e) { + initSectionOptions(e); + } + }); + } catch (TooManyListenersException e) { + throw new RuntimeException(e); + } + + add(m_sectionWidget); + + add(new Label(DecisionTreeUtil.globalize("form_label.match_value"))); + m_matchValueWidget = new SingleSelect(MATCH_OPTION); + m_matchValueWidget.addValidationListener(new NotNullValidationListener()); + m_matchValueWidget.addOption(pleaseSelect); + + try { + m_matchValueWidget.addPrintListener(new PrintListener() { + public void prepare(PrintEvent e) { + initMatchOptions(e); + } + }); + } catch (TooManyListenersException e) { + throw new RuntimeException(e); + } + + add(m_matchValueWidget); + + add(new Label(DecisionTreeUtil.globalize("form_label.target_url"))); + m_targetURLWidget = new TextField(TARGET_URL); + m_targetURLWidget.setSize(60); + add(m_targetURLWidget); + + add(new Label(DecisionTreeUtil.globalize("form_label.target_section"))); + m_targetSectionWidget = new SingleSelect(TARGET_SECTION); + m_targetSectionWidget.addOption(none); + + try { + m_targetSectionWidget.addPrintListener(new PrintListener() { + public void prepare(PrintEvent e) { + initTargetOptions(e); + } + }); + } catch (TooManyListenersException e) { + throw new RuntimeException(e); + } + + add(m_targetSectionWidget); + + addValidationListener(new FormValidationListener() { + public final void validate(final FormSectionEvent event) + throws FormProcessException { + final PageState state = event.getPageState(); + if ("".equals(m_targetURLWidget.getValue(state)) && "".equals(m_targetSectionWidget.getValue(state))) { + String msg = (String) DecisionTreeUtil.globalize("form_validation.target_required").localize(); + throw new FormProcessException(msg); + } + + if (!"".equals(m_targetURLWidget.getValue(state)) && !"".equals(m_targetSectionWidget.getValue(state))) { + String msg = (String) DecisionTreeUtil.globalize("form_validation.duplicate_target").localize(); + throw new FormProcessException(msg); + } + } + }); + } + + /** + * Called on form submission. Check to see if the user clicked the + * cancel button. If they did, don't continue with the form. + */ + public void submitted(FormSectionEvent event) + throws FormProcessException { + PageState state = event.getPageState(); + + if ( m_saveCancelSection.getCancelButton() + .isSelected(state) && m_container != null) { + m_container.onlyShowComponent( + state, DecisionTreeViewTargets.TARGET_TABLE + + m_container.getTypeIDStr()); + throw new FormProcessException( + (String)DecisionTreeUtil + .globalize("tree_section.submission_cancelled") + .localize()); + } + } + + /** + * Called after form has been validated. Create the new SectionTarget and + * assign it to the current DecisionTree. + */ + public void process(FormSectionEvent event) throws FormProcessException { + PageState state = event.getPageState(); + FormData data = event.getFormData(); + + TreeSection section = new TreeSection(new BigDecimal((String)data.get(SECTION))); + SectionOption matchOption = new SectionOption(new BigDecimal((String)data.get(MATCH_OPTION))); + + TreeSection targetSection = null; + String sectionID = (String)data.get(TARGET_SECTION); + if (!"".equals(sectionID)) { + targetSection = new TreeSection(new BigDecimal(sectionID)); + } + + SectionTarget target = null; + if (m_selTarget.getSelectedKey(state) != null) { + BigDecimal id = new BigDecimal(m_selTarget + .getSelectedKey(state).toString()); + target = new SectionTarget(id); + } else { + target = new SectionTarget(); + } + + target.setSection(section); + target.setMatchOption(matchOption); + target.setTargetURL((String)data.get(TARGET_URL)); + target.setTargetSection(targetSection); + + if (m_container != null) { + m_container.onlyShowComponent( + state, + DecisionTreeViewTargets.TARGET_TABLE + + m_container.getTypeIDStr()); + } + } + + public void register(Page p) { + super.register(p); + } +} Added: aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java (rev 0) +++ aplaws/contrib/camden/ccm-ldn-camden-decisiontree/trunk/src/com/arsdigita/camden/cms/contenttypes/ui/TargetTable.java 2007-09-13 14:00:17 UTC (rev 1624) @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2007 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.camden.cms.contenttypes.ui; + + +import org.apache.log4j.Logger; + +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.bebop.table.TableCellRenderer; +import com.arsdigita.bebop.table.TableColumn; +import com.arsdigita.bebop.table.TableColumnModel; +import com.arsdigita.bebop.table.TableModel; +import com.arsdigita.bebop.table.TableModelBuilder; +import com.arsdigita.camden.cms.contenttypes.DecisionTree; +import com.arsdigita.camden.cms.contenttypes.SectionTarget; +import com.arsdigita.camden.cms.contenttypes.SectionTargetCollection; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ItemSelectionModel; +import com.arsdigita.cms.SecurityManager; +import com.arsdigita.cms.dispatcher.Utilities; +import com.arsdigita.util.LockableImpl; + + +/** + * A table that displays the targets for the currently + * selected DecisionTree. + * + * @author Carsten Clasohm + * @version $Id$ + */ +public class TargetTable extends Table +{ + private static final Logger s_log = Logger.getLogger(TargetTable.class); + + // column headings + public static final String COL_SECTION = "Section"; + public static final String COL_MATCH = "Match"; + public static final String COL_EDIT = "Edit"; + public static final String COL_DEL = "Delete"; + + private ItemSelectionModel m_selTree; + + /** + * Constructor. + * + * @param selArticle a selection model that returns the MultiPartArticle + * which holds the sections to display. + */ + public TargetTable(ItemSelectionModel selArticle) { + super(); + m_selTree = selArticle; + + TableColumnModel model = getColumnModel(); + model.add(new TableColumn(0, COL_SECTION)); + model.add(new TableColumn(1, COL_MATCH)); + model.add(new TableColumn(2, COL_EDIT)); + model.add(new TableColumn(3, COL_DEL)); + + model.get(2).setCellRenderer(new SectionTableCellRenderer(true)); + model.get(3).setCellRenderer(new SectionTableCellRenderer(true)); + + setModelBuilder(new OptionTableModelBuilder(m_selTree)); + } + + public void setSectionModel(ItemSelectionModel selSection) { + if (selSection == null) { + s_log.warn("null item model"); + } + } + + /** + * The model builder to generate a suitable model for the OptionTable + */ + protected class OptionTableModelBuilder extends LockableImpl + implements TableModelBuilder + { + protected ItemSelectionModel m_selTree; + + public OptionTableModelBuilder(ItemSelectionModel selTree) { + m_selTree = selTree; + } + + public TableModel makeModel(Table table, PageState state) { + table.getRowSelectionModel().clearSelection(state); + DecisionTree tree = (DecisionTree)m_selTree.getSelectedObject(state); + return new TargetTableModel(table, state, tree); + } + } + + protected class TargetTableModel implements TableModel + { + private TableColumnModel m_colModel; + private SectionTargetCollection m_targets; + private SectionTarget m_target; + + /** Constructor. */ + public TargetTableModel(Table table, PageState state, DecisionTree tree) { + m_colModel = table.getColumnModel(); + m_targets = tree.getTargets(); + } + + /** Return the number of columsn this TableModel has. */ + public int getColumnCount() { + return m_colModel.size(); + } + + /** Move to the next row and return true if the model is now positioned on + * a valid row. + */ + public boolean nextRow() { + if (m_targets.next()) { + m_target = (SectionTarget) m_targets.getTarget(); + return true; + } + return false; + } + + /** Return the data element for the given column and the current row. */ + public Object getElementAt(int columnIndex) { + if (m_colModel == null) { return null; } + + // match columns by name... makes for easier reordering + TableColumn col = m_colModel.get(columnIndex); + String colName = (String) col.getHeaderValue(); + + if (COL_SECTION.equals(colName)) { + return m_target.getSection().getTitle(); + } else if (COL_MATCH.equals(colName)) { + return m_target.getMatchOption().getLabel(); + } else if (COL_EDIT.equals(colName)) { + return "edit"; + } else if (COL_DEL.equals(colName)) { + return "delete"; + } + + return null; + } + + /** Return the key for the given column and the current row. */ + public Object getKeyAt(int columnIndex) { + return m_target.getID(); + } + } + + public class SectionTableCellRenderer extends LockableImpl + implements TableCellRenderer + { + private boolean m_active; + + public SectionTableCellRenderer () { + this(false); + } + + public SectionTableCellRenderer(boolean active) { + m_active = active; + } + + public Component getComponent(Table table, PageState state, + Object value, boolean isSelected, + Object key, int row, int column) { + Component ret = null; + SecurityManager sm = Utilities.getSecurityManager(state); + ContentItem item = (ContentItem)m_selTree.getSelectedObject(state); + + boolean active = m_active && + sm.canAccess(state.getRequest(), SecurityManager.EDIT_ITEM, + item); + + if (value instanceof Component) { + ret = (Component)value; + } else { + if (value == null) { + ret = new Label("", false); + } else { + if (active) { + ret = new ControlLink(value.toString()); + } else { + ret = new Label(value.toString()); + } + } + } + + return ret; + } + } +} |
Author: chrisg23 Date: 2007-09-13 16:00:11 +0200 (Thu, 13 Sep 2007) New Revision: 1622 Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/application.xml aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardResources.properties aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardText.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesStep.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesTable.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/FileOwner.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/Util.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/BasicPerson.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonQueryData.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchForm.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchResults.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/EditObjectForm.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectDetails.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectFinder.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectOwner.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectSearchForm.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectSearchResults.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/SelectObjectsStep.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/SelectableObject.java aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/SelectedObjectsTable.java Log: Wizard steps utility application - generic wizard steps for session based wizards Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/application.xml =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/application.xml (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/application.xml 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> + +<ccm:application name="ccm-wsx-wizard-steps" + prettyName="ReuseableWizard Step Components" + version="1.0.1" + release="1" + xmlns:ccm="http://ccm.redhat.com/ccm-project"> + + <ccm:dependencies> + <ccm:requires name="ccm-wsx-wsgfl-custom" version="1.0.2"/> + + </ccm:dependencies> + + <ccm:directories> + <ccm:directory name="src"/> + + </ccm:directories> + + <ccm:contacts> + <ccm:contact uri="http://wsgfl.westsussex.gov.uk" type="website"/> + <ccm:contact uri="mailto:chr...@we..." type="support"/> + </ccm:contacts> + + <ccm:description> + + </ccm:description> + +</ccm:application> Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardResources.properties =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardResources.properties (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardResources.properties 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,78 @@ +wizard.school_search.label.school_name=Name/DfES No. +wizard.school_search.label.school_search=Search +wizard.school_search.hint.school_name=Enter any part of the school name OR the school DfES number +wizard.school_step.error.no_schools_selected=Please select at least one school before moving to the next step + +wizard.person_search.label.person_first_name=First Name +wizard.person_search.label.person_last_name=Last Name +wizard.person_search.label.person_search=Search +wizard.person_search.hint.person_first_name=Enter any part of the person's first name +wizard.person_search.hint.person_last_name=Enter any part of the person's last name + +wizard.edit_person.label.person_first_name=First Name +wizard.edit_person.label.person_last_name=Last Name +wizard.edit_person.label.person_phone=Phone Number +wizard.edit_person.label.person_email=Email Address +wizard.edit_person.label.person_service=Service +wizard.edit_person.label.person_role=Role + +wizard.edit_visitor.hint.visitor_first_name=Enter the visitor's first name +wizard.edit_visitor.hint.visitor_last_name=Enter the visitor's last name +wizard.edit_visitor.hint.visitor_phone=Enter a contact number for the visitor +wizard.edit_visitor.hint.visitor_email=Enter the main email address for the visitor +wizard.edit_visitor.hint.visitor_service=Enter the service that the visitor is representing - eg NHS +wizard.edit_visitor.hint.visitor_role=Enter the visitor's role within the specified service + +wizard.edit_visitor.error.visitor_first_name_empty=Please enter the visitor's first name +wizard.edit_visitor.error.visitor_last_name_empty=Please enter the visitor's last name +wizard.edit_visitor.error.visitor_phone_empty=Please enter a contact number for the visitor +wizard.edit_visitor.error.visitor_email_empty=Please enter an email address for the visitor +wizard.edit_visitor.error.visitor_service_empty=Please enter the service that the visitor is representing +wizard.edit_visitor.error.visitor_role_empty=Please enter the visitor's role within the specified service + +wizard.visitor_step.error.no_visitors_selected=Please select at least one visitor before moving to the next step + +wizard.attach_files.label.upload=File +wizard.attach_files.label.description=Description (Optional) +wizard.attach_files.label.submit=Save File + +wizard.attach_files.hint.upload=Browse for a file on your local file system using the adjacent browse button +wizard.attach_files.hint.description=Optional description of the file contents. + +wizard.attach_files.error.upload_empty=Please use the browse button above to find a file to add +wizard.attach_files.error.description_too_long=Your description is too long, only 4000 characters can be stored +wizard.attach_files.error.not_uploaded=To add the specified file, use the Add File button before leaving this page. If you don't want to add the file, click Next or Previous. + +wizard.details.label.date=Date of Visit +wizard.details.label.currentFrom=Valid From +wizard.details.label.currentTo=Valid To +wizard.details.label.reason=Report Type +wizard.details.label.keyContact=School Key Contact +wizard.details.label.description=Description + +wizard.details.hint.date=Enter the date that the visit took place - use the drop down lists to select day, month and year +wizard.details.hint.currentFrom=Enter the date from which the report is considered valid - use the drop down lists to select day, month and year +wizard.details.hint.currentTo=Enter the date until when the report is considered valid - use the drop down lists to select day, month and year +wizard.details.hint.reason=Enter the Reason for Visit +wizard.details.hint.keyContact=Enter a contact name for one of the schools that was visited if left blank, headteacher will be specified as the contact. +wizard.details.hint.description=Enter a brief description - this will be displayed in search results + +wizard.details.error.date_in_future=The date of the visit cannot be in the future +wizard.details.error.description_too_long=Your description is too long, only 4000 characters can be stored +wizard.details.error.description_empty=Please enter a description - this is displayed when users search for a visit report + +wizard.types.error.type_null=Please enter a Report Type +wizard.types.error.type_not_unique=There is already a report type of that name +wizard.types.label.type=Report Type +wizard.types.hint.type=Enter the name to be used for the type of report + +wizard.distributionlists.label.name=Name +wizard.distributionlists.label.description=Description +wizard.distributionlists.label.party_search=User or Group name + +wizard.distributionlists.hint.name=Enter a unique name for the distribution list +wizard.distributionlists.hint.description=It will help future users if you can describe the purpose of this distribution list +wizard.distributionlists.hint.party_search=Enter any part of a user or group name + +wizard.distributionlists.error.name_empty=Please enter a name for the distribution list +wizard.distributionlists.error.name_not_unique=There is already a distribution list of that name \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardText.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardText.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/WizardText.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,317 @@ +/* + * Created on 30-Jun-05 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.wizard.ui; + +import com.arsdigita.globalization.GlobalizedMessage; + +/** + * @author cgyg9330 + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +public interface WizardText { + + public static final String BUNDLE_NAME = + "uk.gov.westsussex.wizard.ui.WizardResources"; + + // schools step + + public static final String SCHOOL_SEARCH_LABEL_SCHOOL_NAME = + (String) new GlobalizedMessage("wizard.school_search.label.school_name", + BUNDLE_NAME) + .localize(); + + public static final String SCHOOL_SEARCH_LABEL_SCHOOL_SEARCH = + (String) new GlobalizedMessage("wizard.school_search.label.school_search", + BUNDLE_NAME) + .localize(); + + public static final String SCHOOL_SEARCH_HINT_SCHOOL_NAME = + (String) new GlobalizedMessage("wizard.school_search.hint.school_name", + BUNDLE_NAME) + .localize(); + + public static final String SCHOOLS_STEP_ERROR_NO_SCHOOLS_SELECTED = + (String) new GlobalizedMessage("wizard.school_step.error.no_schools_selected", + BUNDLE_NAME) + .localize(); + + public static final String PERSON_SEARCH_LABEL_PERSON_FIRST_NAME = + (String) new GlobalizedMessage("wizard.person_search.label.person_first_name", + BUNDLE_NAME) + .localize(); + + public static final String PERSON_SEARCH_LABEL_PERSON_LAST_NAME = + (String) new GlobalizedMessage("wizard.person_search.label.person_last_name", + BUNDLE_NAME) + .localize(); + + public static final String PERSON_SEARCH_LABEL_PERSON_SEARCH = + (String) new GlobalizedMessage("wizard.person_search.label.person_search", + BUNDLE_NAME) + .localize(); + + public static final String PERSON_SEARCH_HINT_PERSON_FIRST_NAME = + (String) new GlobalizedMessage("wizard.person_search.hint.person_first_name", + BUNDLE_NAME) + .localize(); + + public static final String PERSON_SEARCH_HINT_PERSON_LAST_NAME = + (String) new GlobalizedMessage("wizard.person_search.hint.person_last_name", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_LABEL_VISITOR_FIRST_NAME = + (String) new GlobalizedMessage("wizard.edit_visitor.label.visitor_first_name", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_LABEL_VISITOR_LAST_NAME = + (String) new GlobalizedMessage("wizard.edit_visitor.label.visitor_last_name", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_LABEL_VISITOR_PHONE = + (String) new GlobalizedMessage("wizard.edit_visitor.label.visitor_phone", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_LABEL_VISITOR_EMAIL = + (String) new GlobalizedMessage("wizard.edit_visitor.label.visitor_email", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_LABEL_VISITOR_SERVICE = + (String) new GlobalizedMessage("wizard.edit_visitor.label.visitor_service", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_LABEL_VISITOR_ROLE = + (String) new GlobalizedMessage("wizard.edit_visitor.label.visitor_role", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_HINT_VISITOR_FIRST_NAME = + (String) new GlobalizedMessage("wizard.edit_visitor.hint.visitor_first_name", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_HINT_VISITOR_LAST_NAME = + (String) new GlobalizedMessage("wizard.edit_visitor.hint.visitor_last_name", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_HINT_VISITOR_PHONE = + (String) new GlobalizedMessage("wizard.edit_visitor.hint.visitor_phone", + BUNDLE_NAME) + .localize(); + + + public static final String EDIT_VISITOR_HINT_VISITOR_EMAIL = + (String) new GlobalizedMessage("wizard.edit_visitor.hint.visitor_email", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_HINT_VISITOR_SERVICE = + (String) new GlobalizedMessage("wizard.edit_visitor.hint.visitor_service", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_HINT_VISITOR_ROLE = + (String) new GlobalizedMessage("wizard.edit_visitor.hint.visitor_role", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_ERROR_VISITOR_FIRST_NAME_EMPTY = + (String) new GlobalizedMessage("wizard.edit_visitor.error.visitor_first_name_empty", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_ERROR_VISITOR_LAST_NAME_EMPTY = + (String) new GlobalizedMessage("wizard.edit_visitor.error.visitor_last_name_empty", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_ERROR_VISITOR_PHONE_EMPTY = + (String) new GlobalizedMessage("wizard.edit_visitor.error.visitor_phone_empty", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_ERROR_VISITOR_EMAIL_EMPTY = + (String) new GlobalizedMessage("wizard.edit_visitor.error.visitor_email_empty", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_ERROR_VISITOR_SERVICE_EMPTY = + (String) new GlobalizedMessage("wizard.edit_visitor.error.visitor_service_empty", + BUNDLE_NAME) + .localize(); + + public static final String EDIT_VISITOR_ERROR_VISITOR_ROLE_EMPTY = + (String) new GlobalizedMessage("wizard.edit_visitor.error.visitor_role_empty", + BUNDLE_NAME) + .localize(); + + public static final String VISITORS_STEP_ERROR_NO_VISITORS_SELECTED = + (String) new GlobalizedMessage("wizard.visitor_step.error.no_visitors_selected", + BUNDLE_NAME) + .localize(); + + public static final String ATTACH_FILES_LABEL_FILE = + (String) new GlobalizedMessage("wizard.attach_files.label.upload", + BUNDLE_NAME) + .localize(); + public static final String ATTACH_FILES_LABEL_DESCRIPTION = + (String) new GlobalizedMessage("wizard.attach_files.label.description", + BUNDLE_NAME) + .localize(); + public static final String ATTACH_FILES_LABEL_UPLOAD = + (String) new GlobalizedMessage("wizard.attach_files.label.submit", + BUNDLE_NAME) + .localize(); + + public static final String ATTACH_FILES_HINT_FILE = + (String) new GlobalizedMessage("wizard.attach_files.hint.upload", + BUNDLE_NAME) + .localize(); + + public static final String ATTACH_FILES_HINT_DESCRIPTION = + (String) new GlobalizedMessage("wizard.attach_files.hint.description", + BUNDLE_NAME) + .localize(); + + public static final String ATTACH_FILES_ERROR_UPLOAD_EMPTY = + (String) new GlobalizedMessage("wizard.attach_files.error.upload_empty", + BUNDLE_NAME) + .localize(); + public static final GlobalizedMessage ATTACH_FILES_ERROR_DESCRIPTION_TOO_LONG = + new GlobalizedMessage("wizard.attach_files.error.description_too_long", + BUNDLE_NAME); + public static final String ATTACH_FILES_ERROR_NOT_UPLOADED = + (String) new GlobalizedMessage("wizard.attach_files.error.not_uploaded", + BUNDLE_NAME) + .localize(); + + public static final String DETAILS_LABEL_VISIT_DATE = + (String) new GlobalizedMessage("wizard.details.label.date", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_LABEL_CURRENT_FROM = + (String) new GlobalizedMessage("wizard.details.label.currentFrom", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_LABEL_CURRENT_TO = + (String) new GlobalizedMessage("wizard.details.label.currentTo", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_LABEL_CONTACT = + (String) new GlobalizedMessage("wizard.details.label.keyContact", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_LABEL_REPORT_TYPE = + (String) new GlobalizedMessage("wizard.details.label.reason", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_LABEL_DESCRIPTION = + (String) new GlobalizedMessage("wizard.details.label.description", + BUNDLE_NAME) + .localize(); + + public static final String DETAILS_HINT_VISIT_DATE = + (String) new GlobalizedMessage("wizard.details.hint.date", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_HINT_CURRENT_FROM = + (String) new GlobalizedMessage("wizard.details.hint.currentFrom", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_HINT_CURRENT_TO = + (String) new GlobalizedMessage("wizard.details.hint.currentTo", + BUNDLE_NAME) + .localize(); + + public static final String DETAILS_HINT_CONTACT = + (String) new GlobalizedMessage("wizard.details.hint.keyContact", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_HINT_REPORT_TYPE = + (String) new GlobalizedMessage("wizard.details.hint.reason", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_HINT_DESCRIPTION = + (String) new GlobalizedMessage("wizard.details.hint.description", + BUNDLE_NAME) + .localize(); + public static final String DETAILS_ERROR_DATE_IN_FUTURE = + (String) new GlobalizedMessage("wizard.details.error.date_in_future", + BUNDLE_NAME) + .localize(); + public static final GlobalizedMessage DETAILS_ERROR_DESCRIPTION_TOO_LONG = + new GlobalizedMessage("wizard.details.error.description_too_long", + BUNDLE_NAME); + + public static final String DETAILS_ERROR_DESCRIPTION_EMPTY= + (String) new GlobalizedMessage("wizard.details.error.description_empty", + BUNDLE_NAME) + .localize(); + + public static final String TYPES_LABEL_TYPE = + (String) new GlobalizedMessage("wizard.types.label.type", + BUNDLE_NAME) + .localize(); + + public static final String TYPES_HINT_TYPE = + (String) new GlobalizedMessage("wizard.types.hint.type", + BUNDLE_NAME) + .localize(); + public static final String TYPES_ERROR_TYPE_EMPTY = + (String) new GlobalizedMessage("wizard.types.error.type_null", + BUNDLE_NAME) + .localize(); + + public static final String TYPES_ERROR_TYPE_NOT_UNIQUE = + (String) new GlobalizedMessage("wizard.types.error.type_not_unique", + BUNDLE_NAME) + .localize(); + + public static final String DISTRIBUTION_LIST_LABEL_NAME = + (String) new GlobalizedMessage("wizard.distributionlists.label.name", + BUNDLE_NAME) + .localize(); + public static final String DISTRIBUTION_LIST_LABEL_DESCRIPTION = + (String) new GlobalizedMessage("wizard.distributionlists.label.description", + BUNDLE_NAME) + .localize(); + + public static final String DISTRIBUTION_LIST_HINT_NAME = + (String) new GlobalizedMessage("wizard.distributionlists.hint.name", + BUNDLE_NAME) + .localize(); + public static final String DISTRIBUTION_LIST_HINT_DESCRIPTION = + (String) new GlobalizedMessage("wizard.distributionlists.hint.description", + BUNDLE_NAME) + .localize(); + public static final String DISTRIBUTION_LIST_LABEL_PARTY_SEARCH = + (String) new GlobalizedMessage("wizard.distributionlists.label.party_search", + BUNDLE_NAME) + .localize(); +public static final String DISTRIBUTION_LIST_HINT_PARTY_SEARCH = + (String) new GlobalizedMessage("wizard.distributionlists.hint.party_search", + BUNDLE_NAME) + .localize(); +public static final String DISTRIBUTION_LIST_ERROR_NAME_EMPTY = + (String) new GlobalizedMessage("wizard.distributionlists.error.name_empty", + BUNDLE_NAME) + .localize();; + +public static final String DISTRIBUTION_LIST_ERROR_NAME_NOT_UNIQUE = + (String) new GlobalizedMessage("wizard.distributionlists.error.name_not_unique", + BUNDLE_NAME) + .localize();; + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesStep.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesStep.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesStep.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,328 @@ +package uk.gov.westsussex.wizard.ui.files; + +import java.io.File; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import uk.gov.westsussex.wizard.ui.WizardText; + +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.FormStep; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleContainer; +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.event.ParameterEvent; +import com.arsdigita.bebop.form.FileUpload; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.form.TextArea; +import com.arsdigita.bebop.parameters.ArrayParameter; +import com.arsdigita.bebop.parameters.BigDecimalParameter; +import com.arsdigita.bebop.parameters.NotEmptyValidationListener; +import com.arsdigita.bebop.parameters.StringInRangeValidationListener; +import com.arsdigita.cms.FileAsset; +import com.arsdigita.cms.dispatcher.SimpleXMLGenerator; +import com.arsdigita.dispatcher.MultipartHttpServletRequest; +import com.arsdigita.domain.DomainCollection; +import com.arsdigita.domain.DomainObjectFactory; +import com.arsdigita.domain.DomainObjectXMLRenderer; +import com.arsdigita.persistence.OID; +import com.arsdigita.persistence.SessionManager; +import com.arsdigita.xml.Element; + +/** + * @author Chris Gilbert <a + * href="mailto:chr...@we...">chr...@we... + * </a> + * + * Wizard step to attach files to an object. + * + * This step must be part of a multipart request form. + * + * The files managed by this step must extend FileAsset + * + */ +public abstract class AttachedFilesStep extends FormStep implements + FormInitListener, FormProcessListener, FormValidationListener { + + private static Logger s_log = Logger.getLogger(AttachedFilesStep.class); + + + private String XMLPrefix; + + private String XMLNameSpace; + + private String elStart; + + private AttachedFilesTable attachedFiles; + + protected FileUpload upload; + + protected TextArea description; + + protected Submit addFile; + + private ArrayParameter newFilesParam = new ArrayParameter(new BigDecimalParameter("newFiles")); + + public AttachedFilesStep(String XMLPrefix, + String XMLNameSpace, String elStart) { + + super("addFiles", new SimpleContainer(XMLPrefix + ":" + elStart + + "Step", XMLNameSpace)); + + this.XMLPrefix = XMLPrefix; + this.XMLNameSpace = XMLNameSpace; + this.elStart = elStart; + attachedFiles = new AttachedFilesTable(newFilesParam, this); + + add(attachedFiles); + + upload = new FileUpload("file", true); + upload + .setMetaDataAttribute("label", + WizardText.ATTACH_FILES_LABEL_FILE); + + upload.setHint(WizardText.ATTACH_FILES_HINT_FILE); + upload.addValidationListener(new NotEmptyValidationListener( + WizardText.ATTACH_FILES_ERROR_UPLOAD_EMPTY) { + public void validate(ParameterEvent e) { + // don't fire validation if the next or previous button of the + // wizard has been pressed + + if (addFile.isSelected(e.getPageState())) { + super.validate(e); + } + } + }); + add(upload); + description = new TextArea("fileDescription"); + description.setCols(20); + description.setRows(5); + description.setMetaDataAttribute("label", + WizardText.ATTACH_FILES_LABEL_DESCRIPTION); + upload.setHint(WizardText.ATTACH_FILES_HINT_DESCRIPTION); + + description.addValidationListener(new StringInRangeValidationListener( + 0, 4000, WizardText.ATTACH_FILES_ERROR_DESCRIPTION_TOO_LONG) { + public void validate(ParameterEvent e) throws FormProcessException { + // don't fire validation if the next or previous button of the + // wizard has been pressed + + if (addFile.isSelected(e.getPageState())) { + super.validate(e); + } + } + }); + add(description); + addFile = new Submit(WizardText.ATTACH_FILES_LABEL_UPLOAD); + addFile.setClassAttr("linkButton"); + add(addFile); + addInitListener(this); + addProcessListener(this); + addValidationListener(this); + } + + /** + * Uploads the file and associates it with the post + * + * @see com.arsdigita.bebop.event.FormProcessListener#process(com.arsdigita.bebop.event.FormSectionEvent) + */ + public void process(FormSectionEvent e) throws FormProcessException { + PageState state = e.getPageState(); + if (addFile.isSelected(state)) { + + FileAsset attachment; + s_log.debug("adding file"); + try { + String fileName = (String) upload.getValue(state); + s_log.debug("filename is " + fileName); + File file = ((MultipartHttpServletRequest) e.getPageState() + .getRequest()).getFile("file"); + s_log.debug("uploaded file is " + file.getName()); + //MimeType mimeType = MimeType.guessMimeTypeFromFile(fileName); + //s_log.debug("mime type is " + mimeType.getMimeType()); + attachment = createFileAsset(); + attachment.loadFromFile(fileName, file, + "application/octet-stream"); + attachment.setDescription((String) description.getValue(state)); + BigDecimal id = attachment.getID(); + BigDecimal[] current = (BigDecimal[]) state.getValue(newFilesParam); + if (current == null) { + state.setValue(newFilesParam, new BigDecimal[] { id }); + + } else { + List files = new ArrayList(Arrays.asList(current)); + files.add(id); + BigDecimal[] newFiles = (BigDecimal[])files.toArray(new BigDecimal[files.size()]); + state.setValue(newFilesParam, newFiles); + + + + + + } + s_log.debug("File Uploaded"); + description.setValue(state, null); + + } catch (Exception ex) { + throw new FormProcessException(ex); + } + } + } + + /** + * Prevent users navigating away from this page unintentionally without + * adding an uploaded file first + */ + public void validate(FormSectionEvent e) throws FormProcessException { + PageState state = e.getPageState(); + if (!addFile.isSelected(state) + && StringUtils.isNotBlank((String) upload.getValue(state))) { + throw new FormProcessException( + WizardText.ATTACH_FILES_ERROR_NOT_UPLOADED); + } + + } + + + + + + /** + * must be called at the end of the wizard when the form is processed set + * the files attached to the post according to the file attachment step + * + * @param post + * @param state + */ + public void attachFiles(PageState state) { + + // if we have finished wizard early then newFilesParam may not have been populated + // we don't want to clear existing files!!! + if (isInitialized(state)) { + BigDecimal[] newFilesArray = (BigDecimal[]) state.getValue(newFilesParam); + List fileList = newFilesArray == null ? Collections.EMPTY_LIST : new ArrayList(Arrays.asList(newFilesArray)); + FileOwner owner = getOwner(state); + owner.updateFiles(fileList); + } + state.setValue(newFilesParam, null); + + } + + public void register(Page p) { + super.register(p); + + p.addGlobalStateParam(newFilesParam); + } + + /* + * (non-Javadoc) + * + * @see com.arsdigita.bebop.event.FormInitListener#init(com.arsdigita.bebop.event.FormSectionEvent) + */ + public void init(FormSectionEvent e) throws FormProcessException { +// + + PageState state = e.getPageState(); + + FileOwner report = getOwner(state); + if (report != null) { + + DomainCollection files = report.getFiles(); + if (files.size() > 0) { + + BigDecimal[] fileIDs = new BigDecimal[new Long(files.size()).intValue()]; + int i = 0; + while (files.next()) { + + FileAsset file = (FileAsset) files.getDomainObject(); + fileIDs[i++] = file.getID(); + } + state.setValue(newFilesParam, fileIDs); + } + } + + } + + public abstract FileOwner getOwner(PageState state); + + + public void getFilesXML(PageState state, Element parent) { + + BigDecimal[] files = (BigDecimal[])state.getValue(newFilesParam); + if (files != null) { + for (int i = 0; i < files.length; i++) { + FileAsset file = getFile(files[i]); + Element linkEl = parent.newChildElement(getXMLPrefix() + ":" + elStart, getXMLNameSpace()); + DomainObjectXMLRenderer xr = new DomainObjectXMLRenderer(linkEl); + xr.setNamespace(getXMLPrefix(), getXMLNameSpace()); + xr.setWrapRoot(false); + xr.setWrapAttributes(true); + xr.setWrapObjects(false); + + // don't know why, but renderer can't find the adapter in the context SimpleXMLGenerator + // bit of a mystery as it is the same one used to render links on articles and that works + // fine. For the moment, a new adapter is declared in the traversals for this application + xr.walk(file, SimpleXMLGenerator.ADAPTER_CONTEXT); + + + + } + + + + + } + + + } + + /** + * specifies the base data object type for the type of file asset managed by + * this table + * + * @return + */ + protected abstract String getSpecificFileObjectType(); + + protected String getXMLNameSpace() { + return XMLNameSpace; + } + + /** + * prefix to be used in generated xml - return null if elements are to be + * unqualified + * + * @return + */ + protected String getXMLPrefix() { + return XMLPrefix; + } + + public Element newElement(String name) { + if (getXMLPrefix() != null) { + return new Element(getXMLPrefix() + ":" + name, getXMLNameSpace()); + } else { + return new Element(name); + } + } + + public FileAsset getFile(BigDecimal id) { + OID oid = new OID(getSpecificFileObjectType(), id); + s_log.debug("oid of file is " + oid.toString()); + return (FileAsset) DomainObjectFactory.newInstance(SessionManager.getSession().retrieve(oid)); + + } + + protected abstract FileAsset createFileAsset(); + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesTable.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesTable.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/AttachedFilesTable.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,183 @@ +/* + * Created on 06-Jul-05 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.wizard.ui.files; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.servlet.ServletException; + +import org.apache.log4j.Logger; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.bebop.parameters.ArrayParameter; +import com.arsdigita.cms.FileAsset; +import com.arsdigita.cms.dispatcher.Utilities; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.xml.Element; + +/** + * Semantic table with delete link. This could be extended to provide additional + * links eg move up/move down + * + * @author Chris Gilbert <a + * href="mailto:chr...@we...">chr...@we... + * </a> + * @version $Revision: 1.1 $ $DateTime: 2004/08/17 23:15:09 $ + */ +public class AttachedFilesTable extends SimpleComponent { + + // I have not implemented an edit link or a move up/down link although the + // data model + // is able to manage this, it seemed like a lot of work for a minor + // requirement for a forum + // currently, files are listed in the order they are added. + + // to add extra links, define new keys for the control event and check key + // value in the + // respond method (see ThreadDisplay for example code) + // + // only issue is that when editing a post, the editor cannot change the + // description easily + // (though they could download the file to their PC, delete the existing one + // and recreate it) + private static Logger s_log = Logger.getLogger(AttachedFilesTable.class); + + protected static final String ACTION_DELETE = "delete"; + + private static final String MOVE_UP = "up"; + + private static final String MOVE_DOWN = "down"; + + private ArrayParameter m_newFiles; + + private AttachedFilesStep m_parent; + + public AttachedFilesTable(ArrayParameter newFiles, AttachedFilesStep parent) { + m_newFiles = newFiles; + m_parent = parent; + + } + + public void respond(PageState state) throws ServletException { + super.respond(state); + + String key = state.getControlEventName(); + BigDecimal value = new BigDecimal(state.getControlEventValue()); + + if (ACTION_DELETE.equals(key)) { + s_log.debug("Remove link pressed - removing object " + value); + BigDecimal[] existingArray = (BigDecimal[]) state.getValue(m_newFiles); + if (existingArray != null) { + List existingList = new ArrayList(Arrays.asList(existingArray)); + + existingList.remove(value); + BigDecimal[] current = (BigDecimal[]) existingList + .toArray(new BigDecimal[existingList.size()]); + + state.setValue(m_newFiles, current); + + } + + } + + if (MOVE_UP.equals(key)) { + BigDecimal[] existingArray = (BigDecimal[]) state.getValue(m_newFiles); + if (existingArray != null) { + + for (int i = 0; i < existingArray.length; i++) { + if (existingArray[i].equals(value)) { + existingArray[i] = existingArray[i - 1]; + existingArray[i - 1] = value; + break; + } + + } + state.setValue(m_newFiles, existingArray); + } + + } + if (MOVE_DOWN.equals(key)) { + BigDecimal[] existingArray = (BigDecimal[]) state.getValue(m_newFiles); + if (existingArray != null) { + + for (int i = 0; i < existingArray.length; i++) { + if (existingArray[i].equals(value)) { + existingArray[i] = existingArray[i + 1]; + existingArray[i + 1] = value; + break; + } + + } + state.setValue(m_newFiles, existingArray); + } + + } + + } + + public void generateXML(PageState state, Element p) { + + Element mainElement = p.newChildElement(m_parent + .newElement("attachedFiles")); + + BigDecimal[] currentFiles = (BigDecimal[]) state.getValue(m_newFiles); + if (currentFiles != null && currentFiles.length != 0) { + // retrieve one at a time - less efficient, but they are in the + // right order + // (link order isn't persisted until this event is saved, so it is + // maintained by the + // array parameter) + + for (int i = 0; i < currentFiles.length; i++) { + + FileAsset file = m_parent.getFile(currentFiles[i]); + Element fileElement = mainElement.newChildElement(m_parent + .newElement("file")); + fileElement.addAttribute("name", file.getName()); + fileElement.addAttribute("url", Utilities.getAssetURL(file)); + fileElement.addAttribute("description", file.getDescription()); + generateActionXML(state, fileElement, file, i == 0, + i == (currentFiles.length - 1)); + i++; + } + + } + + } + + private void generateActionXML(PageState state, Element parent, + FileAsset file, boolean first, boolean last) { + //separate method so that if future links require some logic + //to decide whether to display, it can be implemented here + parent.addAttribute("deleteLink", makeURL(state, ACTION_DELETE, file)); + if (!first) { + parent.addAttribute("moveUp", makeURL(state, MOVE_UP, file)); + } + if (!last) { + parent.addAttribute("moveDown", makeURL(state, MOVE_DOWN, file)); + } + } + + protected String makeURL(PageState state, String action, FileAsset file) { + state.setControlEvent(this, action, file.getID().toString()); + + String url = null; + try { + url = state.stateAsURL(); + } catch (IOException ex) { + throw new UncheckedWrapperException("cannot create url", ex); + } + state.clearControlEvent(); + return url; + } + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/FileOwner.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/FileOwner.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/FileOwner.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,32 @@ +/* + * Created on 27-Nov-2006 + */ +package uk.gov.westsussex.wizard.ui.files; + +import java.util.List; + +import com.arsdigita.domain.DomainCollection; + +/** + * @author chr...@we... + * + * + */ +public interface FileOwner { + + /** + * @return + */ + DomainCollection getFiles(); + + /** + * @param file + * Note - in wizards that create simple items that do not have draft versions, + * the updateFiles method should also set the files as live in order that it + * becomes searchable + */ + void updateFiles(List files); + + + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/Util.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/Util.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/files/Util.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,15 @@ +/* + * Created on 27-Nov-2006 + */ +package uk.gov.westsussex.wizard.ui.files; + +/** + * @author chr...@we... + * + * + */ +public class Util { + + public static void main(String[] args) { + } +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/BasicPerson.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/BasicPerson.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/BasicPerson.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,149 @@ +/* + * Created on 14-Aug-06 + * + * + */ +package uk.gov.westsussex.wizard.ui.people; + +import org.apache.log4j.Logger; + +import uk.gov.westsussex.wizard.ui.selectObjects.SelectableObject; + +import com.arsdigita.xml.Element; + +/** + * @author chr...@we... + * + * + */ +public class BasicPerson implements SelectableObject { + + private static Logger s_log = Logger.getLogger(BasicPerson.class); + private String id; + private String firstName; + private String lastName; + private String email; + private String phone; + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + + public BasicPerson( + String id, + String firstName, + String lastName, + String email, + String phone) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.phone = phone; + } + public String getIdentifier() { + return id; + } + + public String getFirstName() { + return firstName; + } + public String getLastName() { + return lastName; + } + public String getEmailAddress() { + return email; + } + + public String getPhone() { + return phone; + } + + public int compareTo(Object o) { + BasicPerson other = (BasicPerson) o; + + int surnameCompare = + getLastName().toLowerCase().compareTo( + other.getLastName().toLowerCase()); + if (surnameCompare != 0) { + return surnameCompare; + } + int firstNameCompare = + getFirstName().toLowerCase().compareTo( + other.getFirstName().toLowerCase()); + if (firstNameCompare != 0) { + return firstNameCompare; + } + int emailCompare = + getEmailAddress().toLowerCase().compareTo( + other.getEmailAddress().toLowerCase()); + if (emailCompare != 0) { + return emailCompare; + } + return getIdentifier().compareTo(other.getIdentifier()); + + } + + /** + * this is generally consistent with compareTo, in that it is impossible to have + * 2 results that are not equal but return 0 from compareTo as in compareTo + * ids must be equal if the final result is 0 + * + */ + public boolean equals(Object other) { + s_log.debug("Equals "); + if (!other.getClass().equals(BasicPerson.class)) { + return false; + } + BasicPerson searchResult = (BasicPerson) other; + s_log.debug( + "name of this visitor = " + + getFirstName() + + " " + + getLastName() + + " name of other " + + searchResult.getFirstName() + + " " + + searchResult.getLastName()); + s_log.debug( + "ID of this = " + + getIdentifier() + + " ID of other = " + + searchResult.getIdentifier()); + return searchResult.getIdentifier().equals(getIdentifier()); + + } + + public int hashCode() { + return getIdentifier().hashCode(); + } + /* (non-Javadoc) + * @see uk.gov.westsussex.wizard.ui.selectpeople.Person#addListAttributes(com.arsdigita.xml.Element) + */ + public void addListAttributes(Element personElement) { + s_log.debug("adding list attributes for person " + getFirstName() + " " + getLastName()); + personElement.addAttribute("lastName", getLastName()); + personElement.addAttribute( + "firstName", + getFirstName()); + personElement.addAttribute("email", getEmailAddress()); + + } + /* (non-Javadoc) + * @see uk.gov.westsussex.wizard.ui.selectpeople.Person#isEditable() + */ + public boolean isEditable() { + // TODO Auto-generated method stub + return false; + } + /* (non-Javadoc) + * @see uk.gov.westsussex.wizard.ui.selectpeople.Person#addFullAttributes(com.arsdigita.xml.Element) + */ + public void addFullAttributes(Element personEl) { + s_log.debug("adding full attributes for person " + getFirstName() + " " + getLastName()); + addListAttributes(personEl); + personEl.addAttribute("phone", getPhone()); + + + } + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonQueryData.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonQueryData.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonQueryData.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,35 @@ +/* + * Created on 11-Dec-2006 + */ +package uk.gov.westsussex.wizard.ui.people; + +/** + * @author chr...@we... + * + * + */ +public class PersonQueryData { + + private String firstNamePart; + + private String lastNamePart; + + /** + * + */ + public PersonQueryData(String firstNamePart, String lastNamePart) { + this.firstNamePart = firstNamePart; + this.lastNamePart = lastNamePart; + } + + + public String getFirstNamePart() { + return firstNamePart; + } + + + public String getLastNamePart() { + return lastNamePart; + } + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchForm.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchForm.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchForm.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,68 @@ +/* + * Created on 11-Dec-2006 + */ +package uk.gov.westsussex.wizard.ui.people; + +import org.apache.commons.lang.StringUtils; + +import uk.gov.westsussex.wizard.ui.WizardText; +import uk.gov.westsussex.wizard.ui.selectObjects.ObjectSearchForm; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.form.TextField; + +/** + * @author chr...@we... + * + * + */ +public class PersonSearchForm extends ObjectSearchForm { + + private TextField firstName; + private TextField lastName; + + + public PersonSearchForm(RequestLocal queryData, String XMLPrefix, String XMLNameSpace, String elStart) { + super(queryData, XMLPrefix, XMLNameSpace, elStart); + + + } + + /* (non-Javadoc) + * @see uk.gov.westsussex.wizard.ui.selectObjects.ObjectSearchForm#addFields() + */ + protected void addFields() { + + firstName = new TextField("firstNameSrch"); + firstName.setMetaDataAttribute( + "label", + WizardText.PERSON_SEARCH_LABEL_PERSON_FIRST_NAME); + firstName.setHint( + WizardText.PERSON_SEARCH_HINT_PERSON_FIRST_NAME); + add(firstName); + + lastName = new TextField("lastNameSrch"); + lastName.setMetaDataAttribute( + "label", + WizardText.PERSON_SEARCH_LABEL_PERSON_LAST_NAME); + lastName.setHint( + WizardText.PERSON_SEARCH_HINT_PERSON_LAST_NAME); + add(lastName); + + + } + + /* (non-Javadoc) + * @see uk.gov.westsussex.wizard.ui.selectObjects.ObjectSearchForm#populateQuery(com.arsdigita.bebop.PageState, com.arsdigita.bebop.RequestLocal) + */ + protected void populateQuery(PageState state, RequestLocal queryData) { + + PersonQueryData data = new PersonQueryData(StringUtils.strip((String) firstName.getValue(state)),StringUtils.strip((String) lastName.getValue(state))); + queryData.set(state, data); + + } + + + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchResults.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchResults.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/people/PersonSearchResults.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,60 @@ +/* + * Created on 29-Mar-06 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +package uk.gov.westsussex.wizard.ui.people; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import uk.gov.westsussex.wizard.ui.selectObjects.ObjectSearchResults; + +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.parameters.ArrayParameter; +import com.arsdigita.bebop.parameters.StringParameter; + +/** + * @author chr...@we... + * + * Component that displays search results (using whatever search strategy + * is implemented by the subclass). Each result may be selected or + * viewed + */ +public abstract class PersonSearchResults extends ObjectSearchResults { + + + public PersonSearchResults( + RequestLocal queryData, + String XMLPrefix, + String XMLNameSpace, + String elStart, + ArrayParameter selectedPeople, + StringParameter currentPerson) { + + super(queryData, XMLPrefix, XMLNameSpace, elStart, selectedPeople, currentPerson); + + + } + + /* (non-Javadoc) + * @see uk.gov.westsussex.wizard.ui.selectObjects.ObjectSearchResults#search(java.lang.Object) + */ + protected List search(Object queryData, Set errors) { + PersonQueryData data = (PersonQueryData) queryData; + if (data != null) { + return search (data.getFirstNamePart(), data.getLastNamePart(), errors); + } else { + return Collections.EMPTY_LIST; + } + } + + + protected abstract List search(String firstNamePart, String lastNamePart, Set errors); + + + + +} \ No newline at end of file Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/EditObjectForm.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/EditObjectForm.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/EditObjectForm.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,148 @@ +package uk.gov.westsussex.wizard.ui.selectObjects; + +import java.util.HashSet; + +import org.apache.log4j.Logger; + +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.FormSection; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SaveCancelSection; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.kernel.ACSObject; + +/** + * @author Chris Gilbert <a href="mailto:chr...@we...">chr...@we...</a> + * + * Add or edit a visitor who is not in the county directory + * + */ +public abstract class EditObjectForm + extends FormSection + implements FormInitListener, FormSubmissionListener, FormProcessListener { + + private static Logger s_log = Logger.getLogger(EditObjectForm.class); + private Submit save; + private Submit cancel; + + //private SaveCancelSection saveCancel; + + // used to obtain a select url + //private PersonSearchResults results; + private StringParameter currentObject; + private ObjectFinder finder; + + private String elStart; + + public EditObjectForm(String XMLPrefix, String XMLNameSpace, String elStart, StringParameter currentObject, ObjectFinder finder) + { + super( + new SimpleContainer(XMLPrefix + ":" + elStart + "Edit", + XMLNameSpace)); + + this.currentObject = currentObject; + this.finder = finder; + this.elStart = elStart; + addWidgets(); + addInitListener(this); + addSubmissionListener(this); + addProcessListener(this); + + } + + protected void addWidgets() { + save = new Submit(elStart + "Save", "Save"); + cancel = new Submit(elStart + "Cancel", "Cancel"); + save.setClassAttr("linkButton"); + cancel.setClassAttr("linkButton"); + add(save); + add(cancel); + } + + + + public void init(FormSectionEvent e) throws FormProcessException { + PageState state = e.getPageState(); + String objectID = (String)state.getValue(currentObject); + if (objectID != null && isVisible(state)) { + initWidgets (state, objectID); + + } + + } + + + protected abstract void initWidgets (PageState state, String objectID); + + + /* (non-Javadoc) + * @see com.arsdigita.bebop.event.FormCancelListener#cancel(com.arsdigita.bebop.event.FormSectionEvent) + */ + + public void submitted(FormSectionEvent e) throws FormProcessException { + PageState state = e.getPageState(); + if (cancel.isSelected(state)) { + s_log.debug("form cancelled"); + fireCompletionEvent(e.getPageState()); + throw new FormProcessException("cancelled"); + } + + } + + /** + * Set search query in space accessible by results list, and make results list visible + * @see com.arsdigita.bebop.event.FormProcessListener#process(com.arsdigita.bebop.event.FormSectionEvent) + */ + public void process(FormSectionEvent e) throws FormProcessException { +s_log.debug("In Process"); + PageState state = e.getPageState(); + if (isSaveSelected(state)) { + + String id = (String)state.getValue(currentObject); + Object object = null; + + if (id == null) { + s_log.debug("creating a new object"); + object = createObject(); + } else { + s_log.debug("retrieving current selected object"); + object = finder.findObjectForUpdate(id, new HashSet()); + } + s_log.debug("about to update object"); + updateObject(object, state); + s_log.debug("ok - updated object"); + state.setValue(currentObject, getNewObjectIdentifier(object)); + fireCompletionEvent(state); + } + } + + /** + * @param state + */ + public abstract void updateObject(Object object, PageState state); + + /** + * @return + */ + public abstract Object createObject(); + + public abstract String getNewObjectIdentifier(Object object); + + protected boolean isSaveSelected (PageState state) { + return save.isSelected(state); + } + + + + + + + + +} Added: aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectDetails.java =================================================================== --- aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectDetails.java (rev 0) +++ aplaws/contrib/wsx/ccm-wsx-wizard-steps/src/uk/gov/westsussex/wizard/ui/selectObjects/ObjectDetails.java 2007-09-13 14:00:11 UTC (rev 1622) @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * + * The contents of this file are subject to the CCM Public License (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.redhat.com/licenses/ccmpl.html + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for + * the specific language governing rights and limitations under the License. + * + */ + +package uk.gov.westsussex.wizard.ui.selectObjects; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.log4j.Logger; + +import com.arsdigita.bebop.ActionLink; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.RequestLocal; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.parameters.ArrayParameter; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.xml.Element; + +public class ObjectDetails extends SimpleContainer { + + private static Logger s_log = Logger.getLogger(ObjectDetails.class); + + private ObjectFinder finder; + + final private ArrayParameter selectedObjects; + + private StringParameter currentObject; + + private ActionLink editLink; + + private ActionLink selectLink; + + + private RequestLocal selectableObject = new RequestLocal () { + public Object initialValue(PageState state) { + String objectID = (String) state + .getValue(currentObject); + if (objectID != null) { + Set errors = new HashSet(); + s_log.debug("retieving object " + objectID); + return finder.findObject( + objectID, errors); + } else { + return null; + } + } + }; + + public ObjectDetails(String XMLPrefix, String XMLNameSpace, String elStart, + ArrayParameter selectedObjects, StringParameter currentObject, + ObjectFinder finder) { + super(XMLPrefix + ":" + elStart + "Details", XMLNameSpace); + + this.selectedObjects = selectedObjects; + this.currentObject = currentObject; + this.finder = finder; + editLink = new ActionLink("edit") { + public boolean isVisible (PageState state) { + SelectableObject object = (SelectableObject)selectableObject.get(state); + return object != null && object.isEditable(); + + } + }; + editLink.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + fireCompletionEvent(e.getPageState()); + + } + }); + editLink.setClassAttr("linkButton"); + add(editLink); + selectLink = new ActionLink("select") ; + selectLink.addActionListener(new ActionListener() { + + public void ac... [truncated message content] |
From: <chr...@fe...> - 2007-09-13 13:00:18
|
Author: chrisg23 Date: 2007-09-13 15:00:14 +0200 (Thu, 13 Sep 2007) New Revision: 1621 Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentItem.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/TextPageBody.java Log: Sourceforge patch 1707913 - final part of performance improvement changes. Prevent excessively setting content section as the context of contentitems, and don't give text assets a security context (they don't need one as they are never accesed outside their owner) Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentItem.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentItem.java 2007-09-13 12:30:18 UTC (rev 1620) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentItem.java 2007-09-13 13:00:14 UTC (rev 1621) @@ -322,11 +322,8 @@ if (PARENT.equals(name)) { ContentItem ci = (ContentItem) dobj; - if (newVal == null) { + if (newVal != null) { PermissionService.setContext - (ci, ci.getContentSection()); - } else { - PermissionService.setContext (ci.getOID(), ((DataObject) newVal).getOID()); } } @@ -429,22 +426,28 @@ } } + /* + + removed cg - object observer sets context based + on parent whenever parent is updated + protected void afterSave() { super.afterSave(); - + s_log.info("******After Save of object " + getOID()); // Set the object's context to its parent object for // permissioning. if (m_wasNew) { final ACSObject parent = getParent(); - if (parent == null) { + s_log.info("parent is null - set context to content section"); PermissionService.setContext(this, getContentSection()); } else { + s_log.info("parent is " + parent.getOID()); PermissionService.setContext(this, parent); } } } - + */ private void setDefaultContentSection() { s_log.debug("Setting the default content section"); Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/TextPageBody.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/TextPageBody.java 2007-09-13 12:30:18 UTC (rev 1620) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/TextPageBody.java 2007-09-13 13:00:14 UTC (rev 1621) @@ -104,7 +104,9 @@ TextPage item = getTextPage(s); TextAsset t = new TextAsset(); t.setName(item.getName() + "_text_" + item.getID()); - t.setParent(item); + // no need - cg. Text doesn't need a security context, + // and ownership of text is recorded in text_pages + // t.setParent(item); return t; } @@ -118,7 +120,10 @@ protected void updateTextAsset(PageState s, TextAsset a) { TextPage t = getTextPage(s); Assert.assertNotNull(t); - a.setParent(t); + // no need - cg. Text doesn't need a security context, + // and ownership of text is recorded in text_pages + + // a.setParent(t); t.setTextAsset(a); a.save(); t.save(); |
From: <chr...@fe...> - 2007-09-13 13:00:17
|
Author: chrisg23 Date: 2007-09-13 15:00:13 +0200 (Thu, 13 Sep 2007) New Revision: 1620 Modified: aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkPublish.java aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkUnpublish.java Log: Sourceforge patch 1680544 Changes to bulk publish & unpublish: Bulk publish - if a default expiry notification is set in config and an item is published with an end date, then create a notification phase to prevent pulk published items disappearing silently. Bulk unpublish - implement a folder id argument that behaves like the existing argument in BulkPublish. Modified: aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkPublish.java =================================================================== --- aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkPublish.java 2007-09-13 12:17:59 UTC (rev 1619) +++ aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkPublish.java 2007-09-13 13:00:13 UTC (rev 1620) @@ -26,13 +26,17 @@ import com.arsdigita.persistence.OID; import com.arsdigita.persistence.SessionManager; import com.arsdigita.persistence.DataCollection; +import com.arsdigita.workflow.simple.Workflow; import com.arsdigita.domain.DomainObjectFactory; import com.arsdigita.cms.ContentPage; import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.ContentSection; import com.arsdigita.cms.ContentTypeLifecycleDefinition; import com.arsdigita.cms.Folder; +import com.arsdigita.cms.lifecycle.Lifecycle; import com.arsdigita.cms.lifecycle.LifecycleDefinition; +import com.arsdigita.cms.lifecycle.Phase; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.OptionBuilder; @@ -164,6 +168,10 @@ } }.run(); + final int expiryNotification = ContentSection. + getConfig().getDefaultNotificationTime(); + + final Iterator items = toPublish.iterator(); while (items.hasNext()) { final OID oid = (OID) items.next(); @@ -193,7 +201,31 @@ return; } - item.publish(def, new Date()); + ContentItem pending = item.publish(def, new Date()); + final Lifecycle lifecycle = pending.getLifecycle(); + Date endDate = lifecycle.getEndDate(); + if (expiryNotification > 0) { + + if (endDate != null) { + + Date notificationDate = new Date(endDate.getTime() - (long)expiryNotification * 3600000L); + + Phase expirationImminentPhase = + lifecycle.addCustomPhase("expirationImminent", + new Long(notificationDate.getTime()), + new Long(endDate.getTime())); + expirationImminentPhase. + setListenerClassName("com.arsdigita.cms.lifecycle.NotifyLifecycleListener"); + expirationImminentPhase.save(); + } + } + if (ContentSection.getConfig().getDeleteWorkflowAfterPublication()) { + Workflow workflow = Workflow.getObjectWorkflow(item); + if (workflow != null) { + workflow.delete(); + } + } + } }; try { Modified: aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkUnpublish.java =================================================================== --- aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkUnpublish.java 2007-09-13 12:17:59 UTC (rev 1619) +++ aplaws/trunk/ccm-ldn-util/src/com/arsdigita/london/util/cmd/BulkUnpublish.java 2007-09-13 13:00:13 UTC (rev 1620) @@ -21,6 +21,7 @@ import com.arsdigita.london.util.Program; import com.arsdigita.london.util.Transaction; import com.arsdigita.persistence.CompoundFilter; +import com.arsdigita.persistence.Filter; import com.arsdigita.persistence.FilterFactory; import com.arsdigita.persistence.OID; import com.arsdigita.persistence.SessionManager; @@ -29,6 +30,7 @@ import com.arsdigita.cms.ContentPage; import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.Folder; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.OptionBuilder; @@ -56,6 +58,13 @@ .withLongOpt( "types" ) .withDescription( "Restrict unpublishing to items of the specified content types" ) .create( "t" ) ); + options.addOption( + OptionBuilder + .hasArg() + .withLongOpt( "restrictToFolderId" ) + .withDescription( "Restrict publishing to items within the folder with the specified id" ) + .create( "f" ) ); + options.addOption (OptionBuilder .hasArg(false) @@ -65,6 +74,7 @@ } protected void doRun(CommandLine cmdLine) { + final int folderId; final String[] types; final boolean ignoreErrors = cmdLine.hasOption("i"); @@ -79,6 +89,13 @@ types = null; System.out.println( "Unpublishing all live items" ); } + if (cmdLine.hasOption("f")) { + folderId = Integer.parseInt(cmdLine.getOptionValue("f")); + Folder folder = new Folder(new OID(Folder.BASE_DATA_OBJECT_TYPE, folderId)); + System.out.println( "Unpublishing items in folder: " + folder.getDisplayName()); + } else { + folderId = -1; + } final List toUnpublish = new ArrayList(); new Transaction() { @@ -89,12 +106,17 @@ items.addEqualsFilter("version", ContentItem.LIVE); items.addOrder("title"); + FilterFactory filterFactory = items.getFilterFactory(); + + if (folderId >= 0) { + Filter filter = filterFactory.simple(" ancestors like '%/" + folderId + "/%'"); + items.addFilter(filter); + } if( null != types ) { - FilterFactory ff = items.getFilterFactory(); - CompoundFilter or = ff.or(); + CompoundFilter or = filterFactory.or(); for( int i = 0; i < types.length; i++ ) { - or.addFilter( ff.equals( "objectType", types[i] ) ); + or.addFilter( filterFactory.equals( "objectType", types[i] ) ); } items.addFilter( or ); |
From: <chr...@fe...> - 2007-09-13 13:00:17
|
Author: chrisg23 Date: 2007-09-13 15:00:12 +0200 (Thu, 13 Sep 2007) New Revision: 1619 Modified: aplaws/trunk/ccm-ldn-aplaws/src/com/arsdigita/aplaws/ui/TermWidget.java aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/category-step.xsl Log: Sourceforge patch 1793743 changes for aplaws users with config parameter com.arsdigita.cms.category_authoring_add_form=com.arsdigita.aplaws.ui.ItemCategoryPicker to allow categories to be sorted by sortkey or alphabetically - note if you have your own version of category-step.xsl in your theme you need to apply these changes to your theme file in order that tree nodes populated via Ajax are sorted as you expect Modified: aplaws/trunk/ccm-ldn-aplaws/src/com/arsdigita/aplaws/ui/TermWidget.java =================================================================== --- aplaws/trunk/ccm-ldn-aplaws/src/com/arsdigita/aplaws/ui/TermWidget.java 2007-09-13 12:14:51 UTC (rev 1618) +++ aplaws/trunk/ccm-ldn-aplaws/src/com/arsdigita/aplaws/ui/TermWidget.java 2007-09-13 13:00:12 UTC (rev 1619) @@ -37,6 +37,7 @@ import com.arsdigita.xml.XML; import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ContentSection; import java.math.BigDecimal; import java.util.HashMap; @@ -159,6 +160,9 @@ if (sortKey != null) { el.addAttribute("sortKey", sortKey.toString()); } + // sort order attribute added to every node so that we can + // correctly transform xml fragments returned by ajax + el.addAttribute("order", ContentSection.getConfig().getCategoryTreeOrder()); StringBuffer path = new StringBuffer(parent.getAttribute("fullname")); if (path.length() > 0) path.append(" > "); @@ -224,12 +228,13 @@ Element el = generateCategory(parent, root, ids, null); el.addAttribute("fullname", root.getName()); el.addAttribute("node-id", root.getID().toString()); - + el.addAttribute("order", ContentSection.getConfig().getCategoryTreeOrder()); if (Aplaws.getAplawsConfig().ajaxExpandAllBranches()) { // recognisable attribute has to be in the XML for each snippet that is transformed, // hence add it to the parent el.addAttribute("expand", "all" ); } + List roots = (List) children.get(root.getID()); if (null != roots) { Iterator i = roots.iterator(); Modified: aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/category-step.xsl =================================================================== --- aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/category-step.xsl 2007-09-13 12:14:51 UTC (rev 1618) +++ aplaws/trunk/ccm-ldn-aplaws/web/__ccm__/themes/aplaws/category-step.xsl 2007-09-13 13:00:12 UTC (rev 1619) @@ -12,13 +12,39 @@ <xsl:param name="theme-prefix" /> <xsl:template match="cms:emptyPage[@title='childCategories']"> + <xsl:choose> + <xsl:when test="cms:category/@order='sortKey'"> <xsl:apply-templates select="cms:category/cms:category" mode="cms:javascriptCat"> <xsl:with-param name="expand" select="'none'"/> + <xsl:sort data-type="number" select="@sortKey"/> </xsl:apply-templates> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates select="cms:category/cms:category" mode="cms:javascriptCat"> + <xsl:sort data-type="text" select="@name"/> + <xsl:with-param name="expand" select="'none'"/> + </xsl:apply-templates> + </xsl:otherwise> + </xsl:choose> + </xsl:template> <xsl:template match="cms:emptyPage[@title='autoCategories']"> - <xsl:apply-templates select="cms:category" mode="cms:javascriptCat" /> + <xsl:choose> + <xsl:when test="cms:category/@order='sortKey'"> + <xsl:apply-templates select="cms:category" mode="cms:javascriptCat" > + <xsl:with-param name="expand" select="'none'"/> + <xsl:sort data-type="number" select="@sortKey"/> + </xsl:apply-templates> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates select="cms:category" mode="cms:javascriptCat" > + <xsl:sort data-type="text" select="@name"/> + <xsl:with-param name="expand" select="'none'"/> + </xsl:apply-templates> + </xsl:otherwise> + </xsl:choose> + </xsl:template> </xsl:stylesheet> |
Author: chrisg23 Date: 2007-09-13 15:00:10 +0200 (Thu, 13 Sep 2007) New Revision: 1618 Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/SortableList.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/CategoryWidget.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/CategoryTreeModelBuilder.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SortableCategoryList.java aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java aplaws/trunk/ccm-cms/web/__ccm__/static/cms/admin/category-step/category-step.xsl Log: Sourceforge patch 1793743 changes for cms users to allow categories to be sorted by sortkey or alphabetically Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java 2007-09-13 13:00:10 UTC (rev 1618) @@ -34,6 +34,7 @@ import com.arsdigita.bebop.SimpleComponent; import com.arsdigita.bebop.form.DHTMLEditor; +import com.arsdigita.categorization.Category; import com.arsdigita.cms.dispatcher.DefaultTemplateResolver; import com.arsdigita.cms.dispatcher.ItemResolver; import com.arsdigita.cms.dispatcher.MultilingualItemResolver; @@ -47,6 +48,7 @@ import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.parameter.BooleanParameter; import com.arsdigita.util.parameter.ClassParameter; +import com.arsdigita.util.parameter.EnumerationParameter; import com.arsdigita.util.parameter.ErrorList; import com.arsdigita.util.parameter.IntegerParameter; import com.arsdigita.util.parameter.Parameter; @@ -121,6 +123,7 @@ private final Parameter m_deleteLifecycleWhenComplete; private final Parameter m_deleteExpiryNotificationsWhenSent; private final Parameter m_deleteWorkflowNotificationsWhenSent; + private final Parameter m_categoryTreeOrdering; /** * Do not instantiate this class directly. @@ -341,6 +344,15 @@ ("com.arsdigita.cms.delete_workflow_notification_when_sent", Parameter.OPTIONAL, new Boolean(false)); + m_categoryTreeOrdering = new EnumerationParameter + ("com.arsdigita.cms.category_tree_order", + Parameter.OPTIONAL, Category.SORT_KEY ); + + // 2 valid values at the moment - enumeration used rather than boolean in case other + // possible orders are deemed valid + ((EnumerationParameter)m_categoryTreeOrdering).put("SortKey", Category.SORT_KEY ); + ((EnumerationParameter)m_categoryTreeOrdering).put("Alphabetical", Category.NAME); + register(m_templateRootPath); register(m_defaultItemTemplatePath); register(m_defaultFolderTemplatePath); @@ -386,6 +398,7 @@ register(m_deleteLifecycleWhenComplete); register(m_deleteExpiryNotificationsWhenSent); register(m_deleteWorkflowNotificationsWhenSent); + register(m_categoryTreeOrdering); loadInfo(); } @@ -724,4 +737,7 @@ return ((Boolean)get(m_deleteWorkflowNotificationsWhenSent)).booleanValue(); } + public String getCategoryTreeOrder () { + return (String)get(m_categoryTreeOrdering); + } } Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties 2007-09-13 13:00:10 UTC (rev 1618) @@ -222,3 +222,8 @@ com.arsdigita.cms.delete_workflow_notification_when_sent.purpose=Decide whether successfully sent notifications and messages should be deleted from the system com.arsdigita.cms.delete_workflow_notification_when_sent.example=true|false com.arsdigita.cms.delete_workflow_notification_when_sent.format=[boolean] + +com.arsdigita.cms.category_tree_order.title=Ordering for nodes in assign category tree +com.arsdigita.cms.category_tree_order.purpose=Decide whether entries should be ordered alphabetically or according to sort key (maintained in category admin tab in content centre) +com.arsdigita.cms.category_tree_order.example=SortKey|Alphabetical +com.arsdigita.cms.category_tree_order.format=[string] Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/SortableList.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/SortableList.java 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/SortableList.java 2007-09-13 13:00:10 UTC (rev 1618) @@ -59,16 +59,20 @@ private static final String SELECT_EVENT = "s"; protected static final String PREV_EVENT = "prev"; protected static final String NEXT_EVENT = "next"; - //private boolean m_sortItems; + private boolean m_sortItems; /** * This just makes a standard * {@link SortableList} */ public SortableList(ParameterSingleSelectionModel model) { - super(model); + this(model, false); } + public SortableList(ParameterSingleSelectionModel model, boolean suppressSort) { + super(model); + m_sortItems = !suppressSort; + } /** * This geneates the XML as specified by the arguments pass in to * the constructor. @@ -99,8 +103,10 @@ do { Element item = list.newChildElement (BebopConstants.BEBOP_CELL, BEBOP_XML_NS); - item.addAttribute("configure", "true"); + if (m_sortItems) { + item.addAttribute("configure", "true"); + } String key = m.getKey(); Assert.assertNotNull(key); Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/CategoryWidget.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/CategoryWidget.java 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/authoring/CategoryWidget.java 2007-09-13 13:00:10 UTC (rev 1618) @@ -30,6 +30,7 @@ import com.arsdigita.categorization.Category; import com.arsdigita.categorization.CategoryCollection; import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ContentSection; import java.util.Set; import java.util.HashSet; import java.util.Map; @@ -124,6 +125,10 @@ if (sortKey != null) { el.addAttribute("sortKey", sortKey.toString()); } + // sort order attribute added to every node in order that same xsl may + // be used to transform xml fragments returned by ajax in the Aplaws + // extension + el.addAttribute("order", ContentSection.getConfig().getCategoryTreeOrder()); String fullname = path == null ? "/" : path + " > " + cat.getName(); el.addAttribute("fullname", fullname); Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/CategoryTreeModelBuilder.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/CategoryTreeModelBuilder.java 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/CategoryTreeModelBuilder.java 2007-09-13 13:00:10 UTC (rev 1618) @@ -58,8 +58,8 @@ final ContentSection section = CMS.getContext().getContentSection(); final Category root = Category.getRootForObject(section, getUseContext(state)); - - final CategoryTreeModelLite model = new CategoryTreeModelLite(root, "sortKey"); + String order = ContentSection.getConfig().getCategoryTreeOrder(); + final CategoryTreeModelLite model = new CategoryTreeModelLite(root, order); return model; } Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SortableCategoryList.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SortableCategoryList.java 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SortableCategoryList.java 2007-09-13 13:00:10 UTC (rev 1618) @@ -23,6 +23,7 @@ import com.arsdigita.bebop.parameters.BigDecimalParameter; import com.arsdigita.categorization.Category; import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ContentSection; import com.arsdigita.cms.SecurityManager; import com.arsdigita.cms.ui.SortableList; import com.arsdigita.domain.DataObjectNotFoundException; @@ -69,7 +70,7 @@ */ public SortableCategoryList(final CategoryRequestLocal parent) { super(new ParameterSingleSelectionModel - (new BigDecimalParameter(CHILDREN))); + (new BigDecimalParameter(CHILDREN)), !Category.SORT_KEY.equals(ContentSection.getConfig().getCategoryTreeOrder())); m_parent = parent; Modified: aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java =================================================================== --- aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/src/com/arsdigita/cms/ui/category/SubcategoryList.java 2007-09-13 13:00:10 UTC (rev 1618) @@ -28,6 +28,7 @@ import com.arsdigita.bebop.list.ListModelBuilder; import com.arsdigita.categorization.Category; import com.arsdigita.categorization.CategoryCollection; +import com.arsdigita.cms.ContentSection; import com.arsdigita.cms.util.GlobalizationUtil; import com.arsdigita.util.LockableImpl; @@ -79,7 +80,10 @@ if (category != null && category.hasChildCategories()) { CategoryCollection children = category.getChildren(); - children.addOrder("link." + Category.SORT_KEY); + String order = ContentSection.getConfig().getCategoryTreeOrder(); + order = order = Category.SORT_KEY.equals(order) ? "link." + order : order; + children.addOrder(order); + // children.addOrder("link." + Category.SORT_KEY); return new CategoryCollectionListModel(children); } else { return List.EMPTY_MODEL; Modified: aplaws/trunk/ccm-cms/web/__ccm__/static/cms/admin/category-step/category-step.xsl =================================================================== --- aplaws/trunk/ccm-cms/web/__ccm__/static/cms/admin/category-step/category-step.xsl 2007-09-13 11:23:25 UTC (rev 1617) +++ aplaws/trunk/ccm-cms/web/__ccm__/static/cms/admin/category-step/category-step.xsl 2007-09-13 13:00:10 UTC (rev 1618) @@ -159,9 +159,19 @@ </span> </div> <div id="catCh{@node-id}" style="margin-left: 20px; display: {$expand}"> + <xsl:choose> + <xsl:when test="@order='sortKey'"> <xsl:apply-templates select="cms:category" mode="cms:javascriptCat"> <xsl:sort data-type="number" select="@sortKey"/> </xsl:apply-templates> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates select="cms:category" mode="cms:javascriptCat"> + <xsl:sort data-type="text" select="@name"/> + </xsl:apply-templates> + </xsl:otherwise> + </xsl:choose> + </div> </xsl:template> |
From: <ssk...@fe...> - 2007-09-13 11:35:00
|
Author: sskracic Date: 2007-09-13 13:25:09 +0200 (Thu, 13 Sep 2007) New Revision: 1617 Modified: aplaws/users/sskracic/test.txt Log: Testing commit notifications, please ignore. Modified: aplaws/users/sskracic/test.txt =================================================================== --- aplaws/users/sskracic/test.txt 2007-09-13 10:45:39 UTC (rev 1616) +++ aplaws/users/sskracic/test.txt 2007-09-13 11:25:09 UTC (rev 1617) @@ -4,3 +4,4 @@ More testing to follow ... Commit notification test. One more spam to go (hopefully, the last one). +Unfortunately the wish from the line above not didn't come true. |
From: <ssk...@fe...> - 2007-09-06 08:00:16
|
Author: sskracic Date: 2007-09-06 10:00:11 +0200 (Thu, 06 Sep 2007) New Revision: 1613 Modified: aplaws/users/sskracic/test.txt Log: Testing commit notifications - please ignore ... Modified: aplaws/users/sskracic/test.txt =================================================================== --- aplaws/users/sskracic/test.txt 2007-09-06 07:51:44 UTC (rev 1612) +++ aplaws/users/sskracic/test.txt 2007-09-06 08:00:11 UTC (rev 1613) @@ -2,3 +2,4 @@ Another line. The third line. More testing to follow ... +Commit notification test. |
From: <cl...@fe...> - 2007-09-06 08:00:16
|
Author: clasohm Date: 2007-09-06 10:00:09 +0200 (Thu, 06 Sep 2007) New Revision: 1612 Added: aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.classpath aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.project Modified: aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/ Log: added Eclipse project files Property changes on: aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk ___________________________________________________________________ Name: svn:ignore - build .classpath .project + build .settings Added: aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.classpath =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.classpath (rev 0) +++ aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.classpath 2007-09-06 08:00:09 UTC (rev 1612) @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/ccm-cms"/> + <classpathentry combineaccessrules="false" kind="src" path="/ccm-core"/> + <classpathentry combineaccessrules="false" kind="src" path="/ccm-ldn-rss"/> + <classpathentry combineaccessrules="false" kind="src" path="/ccm-ldn-util"/> + <classpathentry combineaccessrules="false" kind="src" path="/ccm-ldn-navigation"/> + <classpathentry kind="output" path="bin"/> +</classpath> Added: aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.project =================================================================== --- aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.project (rev 0) +++ aplaws/contrib/camden/ccm-ldn-camden-consultation/trunk/.project 2007-09-06 08:00:09 UTC (rev 1612) @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>ccm-ldn-camden-consultation</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> |
From: <ap...@vh...> - 2006-10-11 14:06:34
|
Author: apevec Date: 2006-10-11 15:57:08 +0200 (Wed, 11 Oct 2006) New Revision: 1344 Modified: trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig.java trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig_parameter.properties trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig.java trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig_parameter.properties trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig.java trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig_parameter.properties trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig.java trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig_parameter.properties trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig.java trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig_parameter.properties Log: remove leading slashes from resource: URLs to make them compatible with org.jboss.net.protocol.resource.ResourceURLConnection Modified: trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java =================================================================== --- trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig.java 2006-10-11 13:57:08 UTC (rev 1344) @@ -182,7 +182,7 @@ m_itemAdapters = new URLParameter ("com.arsdigita.cms.item_adapters", Parameter.REQUIRED, - new URL("resource:///WEB-INF/resources/cms-item-adapters.xml")); + new URL("resource:WEB-INF/resources/cms-item-adapters.xml")); } catch (MalformedURLException ex) { throw new UncheckedWrapperException("Cannot parse URL", ex); } Modified: trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties =================================================================== --- trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-cms/src/com/arsdigita/cms/ContentSectionConfig_parameter.properties 2006-10-11 13:57:08 UTC (rev 1344) @@ -49,9 +49,9 @@ com.arsdigita.cms.use_section_categories.format=[boolean] com.arsdigita.cms.item_adapters.title=Item Adapters File -com.arsdigita.cms.item_adapters.purpose=The path to an XML file containing adapter specifications. -com.arsdigita.cms.item_adapters.example=/WEB-INF/resources/adapters.xml -com.arsdigita.cms.item_adapters.format=[string] +com.arsdigita.cms.item_adapters.purpose=The URL to an XML resource containing adapter specifications. +com.arsdigita.cms.item_adapters.example=resource:WEB-INF/resources/cms-item-adapters.xml +com.arsdigita.cms.item_adapters.format=[url] com.arsdigita.cms.default_content_section.title = Default Content Section com.arsdigita.cms.default_content_section.purpose = The name of the default content section Modified: trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java =================================================================== --- trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig.java 2006-10-11 13:57:08 UTC (rev 1344) @@ -77,7 +77,7 @@ ("com.arsdigita.forum.traversal_adapters", Parameter.REQUIRED, new URL(null, - "resource:///WEB-INF/resources/forum-adapters.xml")); + "resource:WEB-INF/resources/forum-adapters.xml")); } catch (MalformedURLException ex) { throw new UncheckedWrapperException("Cannot parse URL", ex); } Modified: trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties =================================================================== --- trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-forum/src/com/arsdigita/forum/ForumConfig_parameter.properties 2006-10-11 13:57:08 UTC (rev 1344) @@ -1,7 +1,7 @@ com.arsdigita.forum.traversal_adapters.title=XML renderer rules com.arsdigita.forum.traversal_adapters.purpose=Rules for configuring information in generated XML -com.arsdigita.forum.traversal_adapters.format=[filename] -com.arsdigita.forum.traversal_adapters.example=/WEB-INF/resources/forum-adapters.xml +com.arsdigita.forum.traversal_adapters.format=[url] +com.arsdigita.forum.traversal_adapters.example=resource:WEB-INF/resources/forum-adapters.xml com.arsdigita.forum.digest_user_email.title=Digest user email address com.arsdigita.forum.digest_user_email.purpose=Email address of the user sending digest emails Modified: trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig.java =================================================================== --- trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig.java 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig.java 2006-10-11 13:57:08 UTC (rev 1344) @@ -53,7 +53,7 @@ ("com.arsdigita.london.atoz.traversal_adapters", Parameter.REQUIRED, new URL(null, - "resource:///WEB-INF/resources/atoz-adapters.xml")); + "resource:WEB-INF/resources/atoz-adapters.xml")); } catch (MalformedURLException ex) { throw new UncheckedWrapperException("Cannot parse URL", ex); } Modified: trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig_parameter.properties =================================================================== --- trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig_parameter.properties 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-atoz/src/com/arsdigita/london/atoz/AtoZConfig_parameter.properties 2006-10-11 13:57:08 UTC (rev 1344) @@ -1,7 +1,7 @@ com.arsdigita.london.atoz.traversal_adapters.title=XML renderer rules com.arsdigita.london.atoz.traversal_adapters.purpose=Rules for configuring information in generated XML -com.arsdigita.london.atoz.traversal_adapters.format=[filename] -com.arsdigita.london.atoz.traversal_adapters.example=/WEB-INF/resources/atoz-adapters.xml +com.arsdigita.london.atoz.traversal_adapters.format=[url] +com.arsdigita.london.atoz.traversal_adapters.example=resource:WEB-INF/resources/atoz-adapters.xml com.arsdigita.london.atoz.root_category_picker.title=Root Category Picker com.arsdigita.london.atoz.root_category_picker.purpose=The UI widget for the Root Category Picker com.arsdigita.london.atoz.root_category_picker.format=[class] @@ -9,4 +9,4 @@ com.arsdigita.london.atoz.use_subsite_specific_navigation_category.title=Make AtoZ use subsite-specific navigation categories com.arsdigita.london.atoz.use_subsite_specific_navigation_category.purpose=Set this to yes, and the AtoZ will use the subsite-specific navigation categories if you define *any* CategoryProvider com.arsdigita.london.atoz.use_subsite_specific_navigation_category.format=[boolean] -com.arsdigita.london.atoz.use_subsite_specific_navigation_category.example=true \ No newline at end of file +com.arsdigita.london.atoz.use_subsite_specific_navigation_category.example=true Modified: trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig.java =================================================================== --- trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig.java 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig.java 2006-10-11 13:57:08 UTC (rev 1344) @@ -105,7 +105,7 @@ ("com.arsdigita.london.navigation.traversal_adapters", Parameter.REQUIRED, new URL(null, - "resource:///WEB-INF/resources/navigation-adapters.xml")); + "resource:WEB-INF/resources/navigation-adapters.xml")); } catch (MalformedURLException ex) { throw new UncheckedWrapperException("Cannot parse URL", ex); } Modified: trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig_parameter.properties =================================================================== --- trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig_parameter.properties 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-navigation/src/com/arsdigita/london/navigation/NavigationConfig_parameter.properties 2006-10-11 13:57:08 UTC (rev 1344) @@ -40,8 +40,8 @@ com.arsdigita.london.navigation.traversal_adapters.title=XML renderer rules com.arsdigita.london.navigation.traversal_adapters.purpose=Rules for configuring information in generated XML -com.arsdigita.london.navigation.traversal_adapters.format=[filename] -com.arsdigita.london.navigation.traversal_adapters.example=/WEB-INF/resources/navigation-traversal-adapters.xml +com.arsdigita.london.navigation.traversal_adapters.format=[url] +com.arsdigita.london.navigation.traversal_adapters.example=resource:WEB-INF/resources/navigation-adapters.xml com.arsdigita.london.navigation.category_menu_show_nephews.title=Show nephew categories in CategoryMenu com.arsdigita.london.navigation.category_menu_show_nephews.purpose=Whether CategoryMenu should display the categories who are nephews to the current category Modified: trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig.java =================================================================== --- trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig.java 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig.java 2006-10-11 13:57:08 UTC (rev 1344) @@ -60,7 +60,7 @@ "com.arsdigita.london.portal.traversal_adapters", Parameter.REQUIRED, new URL(null, - "resource:///WEB-INF/resources/portal-adapters.xml")); + "resource:WEB-INF/resources/portal-adapters.xml")); } catch (MalformedURLException ex) { throw new UncheckedWrapperException("Cannot parse URL", ex); } Modified: trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig_parameter.properties =================================================================== --- trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig_parameter.properties 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-portal/src/com/arsdigita/london/portal/WorkspaceConfig_parameter.properties 2006-10-11 13:57:08 UTC (rev 1344) @@ -1,7 +1,7 @@ com.arsdigita.london.portal.traversal_adapters.title=XML renderer rules com.arsdigita.london.portal.traversal_adapters.purpose=Rules for configuring information in generated XML -com.arsdigita.london.portal.traversal_adapters.format=[filename] -com.arsdigita.london.portal.traversal_adapters.example=/WEB-INF/resources/portal-traversal-adapters.xml +com.arsdigita.london.portal.traversal_adapters.format=[url] +com.arsdigita.london.portal.traversal_adapters.example=resource:WEB-INF/resources/portal-adapters.xml com.arsdigita.london.portal.default_layout.title=Default workspace layout com.arsdigita.london.portal.default_layout.purpose=Default column layout for workspace portals Modified: trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig.java =================================================================== --- trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig.java 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig.java 2006-10-11 13:57:08 UTC (rev 1344) @@ -53,7 +53,7 @@ ("com.arsdigita.london.subsite.traversal_adapters", Parameter.REQUIRED, new URL(null, - "resource:///WEB-INF/resources/subsite-adapters.xml")); + "resource:WEB-INF/resources/subsite-adapters.xml")); } catch (MalformedURLException ex) { throw new UncheckedWrapperException("Cannot parse URL", ex); } Modified: trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig_parameter.properties =================================================================== --- trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig_parameter.properties 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-subsite/src/com/arsdigita/london/subsite/SubsiteConfig_parameter.properties 2006-10-11 13:57:08 UTC (rev 1344) @@ -1,7 +1,7 @@ com.arsdigita.london.subsite.traversal_adapters.title=XML renderer rules com.arsdigita.london.subsite.traversal_adapters.purpose=Rules for configuring information in generated XML -com.arsdigita.london.subsite.traversal_adapters.format=[filename] -com.arsdigita.london.subsite.traversal_adapters.example=/WEB-INF/resources/subsite-traversal-adapters.xml +com.arsdigita.london.subsite.traversal_adapters.format=[url] +com.arsdigita.london.subsite.traversal_adapters.example=resource:WEB-INF/resources/subsite-adapters.xml com.arsdigita.london.subsite.front_page_application.title=Front Page Application (Subsite) com.arsdigita.london.subsite.front_page_application.purpose=The Application to use as the front (home) page for a subsite Modified: trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig.java =================================================================== --- trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig.java 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig.java 2006-10-11 13:57:08 UTC (rev 1344) @@ -46,7 +46,7 @@ ("com.arsdigita.london.terms.traversal_adapters", Parameter.REQUIRED, new URL(null, - "resource:///WEB-INF/resources/terms-adapters.xml")); + "resource:WEB-INF/resources/terms-adapters.xml")); } catch (MalformedURLException ex) { throw new UncheckedWrapperException("Cannot parse URL", ex); } Modified: trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig_parameter.properties =================================================================== --- trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig_parameter.properties 2006-10-10 11:40:46 UTC (rev 1343) +++ trunk/ccm-ldn-terms/src/com/arsdigita/london/terms/TermsConfig_parameter.properties 2006-10-11 13:57:08 UTC (rev 1344) @@ -1,7 +1,7 @@ com.arsdigita.london.terms.traversal_adapters.title=XML renderer rules com.arsdigita.london.terms.traversal_adapters.purpose=Rules for configuring information in generated XML -com.arsdigita.london.terms.traversal_adapters.format=[filename] -com.arsdigita.london.terms.traversal_adapters.example=/WEB-INF/resources/tersm-adapters.xml +com.arsdigita.london.terms.traversal_adapters.format=[url] +com.arsdigita.london.terms.traversal_adapters.example=resource:WEB-INF/resources/terms-adapters.xml com.arsdigita.london.terms.default_domain.title=Default domain com.arsdigita.london.terms.default_domain.purpose=The default navigation domain |
From: <ssk...@vh...> - 2006-10-10 11:50:01
|
Author: sskracic Date: 2006-10-10 13:40:46 +0200 (Tue, 10 Oct 2006) New Revision: 1343 Modified: releases/1.0.4/ccm-cms/application.xml releases/1.0.4/ccm-cms/src/com/arsdigita/cms/SecurityManager.java Log: Integrated r1342 from trunk - corrected permission check in SecurityManager. The bug manifested as an annoying redirect to login screen when not logged in and trying to access the index page. Modified: releases/1.0.4/ccm-cms/application.xml =================================================================== --- releases/1.0.4/ccm-cms/application.xml 2006-10-10 09:41:56 UTC (rev 1342) +++ releases/1.0.4/ccm-cms/application.xml 2006-10-10 11:40:46 UTC (rev 1343) @@ -3,7 +3,7 @@ name="ccm-cms" prettyName="Red Hat CCM Content Management System" version="6.4.0" - release="2" + release="3" webapp="ROOT"> <ccm:dependencies> <ccm:requires name="ccm-core" version="6.3.0" relation="ge"/> Modified: releases/1.0.4/ccm-cms/src/com/arsdigita/cms/SecurityManager.java =================================================================== --- releases/1.0.4/ccm-cms/src/com/arsdigita/cms/SecurityManager.java 2006-10-10 09:41:56 UTC (rev 1342) +++ releases/1.0.4/ccm-cms/src/com/arsdigita/cms/SecurityManager.java 2006-10-10 11:40:46 UTC (rev 1343) @@ -297,13 +297,13 @@ } /** - * Returns true if the specified party has the READ permission on the + * Returns true if the specified party can access authoring UI in the * current content section. False otherwise. * * @pre m_section != null **/ protected boolean canViewAdminPages(Party party) { - return (hasPermission(party, PrivilegeDescriptor.READ)); + return (hasPermission(party, CMS_PREVIEW_ITEM)); } /** |