Moving to root below branch label

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2005-12-08 07:13:07 +00:00
commit d051d1153c
920 changed files with 98871 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Object that can be used as a backing bean for components in the zoo
*
* @author gavinc
*/
public class DummyBean
{
private static Log logger = LogFactory.getLog(DummyBean.class);
private String name;
private Properties properties;
public DummyBean()
{
this.properties = new Properties();
this.properties.put("one", "This is 1");
this.properties.put("two", "This is 2");
this.properties.put("three", "This is 3");
this.properties.put("four", "This is 4");
}
public Properties getProperties()
{
return this.properties;
}
/**
* @return Returns the name.
*/
public String getName()
{
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name)
{
this.name = name;
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
StringBuilder builder = new StringBuilder(super.toString());
builder.append(" (name=").append(this.name);
builder.append(" properties=").append(this.properties).append(")");
return builder.toString();
}
/**
* Method to call on form submit buttons
*/
public void submit()
{
if (logger.isDebugEnabled())
logger.debug("Submit called on DummyBean, state = " + toString());
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import javax.faces.event.ActionEvent;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.UIBreadcrumb;
import org.apache.log4j.Logger;
/**
* @author kevinr
*/
public class TestList
{
/**
* Constructor
*/
public TestList()
{
// Create test data rows
Calendar date = new GregorianCalendar(1999, 1, 5);
rows.add(new TestRow("monkey", 5, true, 0.1f, date.getTime()));
date = new GregorianCalendar(2000, 12, 5);
rows.add(new TestRow("biscuit", 15, true, 0.2f, date.getTime()));
date = new GregorianCalendar(1999, 11, 15);
rows.add(new TestRow("HORSEY", 23, false, 0.333f, date.getTime()));
date = new GregorianCalendar(2003, 11, 11);
rows.add(new TestRow("thing go here", 5123, true, 0.999f, date.getTime()));
date = new GregorianCalendar(1999, 2, 3);
rows.add(new TestRow("I like docs", -5, false, 0.333f, date.getTime()));
date = new GregorianCalendar(2005, 1, 1);
rows.add(new TestRow("Document", 1235, false, 12.0f, date.getTime()));
date = new GregorianCalendar(1998, 8, 8);
rows.add(new TestRow("1234567890", 52, false, 5.0f, date.getTime()));
date = new GregorianCalendar(1997, 9, 30);
rows.add(new TestRow("space", 77, true, 17.5f, date.getTime()));
date = new GregorianCalendar(2001, 7, 15);
rows.add(new TestRow("House", 12, true, 0.4f, date.getTime()));
date = new GregorianCalendar(2002, 5, 28);
rows.add(new TestRow("Baboon", 14, true, -0.888f, date.getTime()));
date = new GregorianCalendar(2003, 11, 11);
rows.add(new TestRow("Woof", 0, true, 0.0f, date.getTime()));
}
public List getRows()
{
return this.rows;
}
public void clickBreadcrumb(ActionEvent event)
{
if (event.getComponent() instanceof UIBreadcrumb)
{
s_logger.debug("clickBreadcrumb action listener called, path now: " + ((UIBreadcrumb)event.getComponent()).getValue());
}
}
public void clickActionLink(ActionEvent event)
{
s_logger.debug("clickActionLink");
}
public void clickNameLink(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String value = params.get("name");
if (value != null)
{
s_logger.debug("clicked item in list: " + value);
}
}
private final static Logger s_logger = Logger.getLogger(TestList.class);
private List<TestRow> rows = new ArrayList<TestRow>();;
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest;
import java.util.Date;
/**
* @author kevinr
*/
public class TestRow
{
/**
* Test a row bean with various data types
*/
public TestRow(String name, int count, boolean valid, float relevance, Date created)
{
this.name = name;
this.count = count;
this.valid = valid;
this.relevance = relevance;
this.created = created;
}
public String getName()
{
return name;
}
public int getCount()
{
return count;
}
public boolean getValid()
{
return valid;
}
public float getRelevance()
{
return relevance;
}
public Date getCreated()
{
return created;
}
public void setCreated(Date date)
{
this.created = date;
}
public TestRow getObject()
{
return this;
}
private String name;
private int count;
private boolean valid;
private float relevance;
private Date created;
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.model.SelectItem;
/**
* Class representing a single User bean instance.
*
* @author kevinr
*/
public class User implements Cloneable
{
public User()
{
setRoles(new ArrayList<String>(4));
}
public User(String username, String password, String name, String[] roles, Date joined)
{
setUsername(username);
setPassword(password);
setName(name);
setDateJoined(joined);
List<String> rolesList = new ArrayList<String>(roles.length);
for (int i=0; i<roles.length; i++)
{
rolesList.add(roles[i]);
}
setRoles(rolesList);
}
/**
* Private copy constructor
*
* @param u User to clone
*/
private User(User u)
{
setUsername(u.getUsername());
setPassword(u.getPassword());
setName(u.getName());
setDateJoined(u.getDateJoined());
setRoles(new ArrayList<String>(u.getRoles()));
}
/**
* @see java.lang.Object#clone()
*/
protected Object clone() throws CloneNotSupportedException
{
// invoke copy constructor
return new User(this);
}
/**
* Get the username
*
* @return the username
*/
public String getUsername()
{
return m_username;
}
/**
* Set the username
*
* @param username the username
*/
public void setUsername(String username)
{
m_username = username;
}
/**
* Get the name
*
* @return the name
*/
public String getName()
{
return m_name;
}
/**
* Set the name
*
* @param name the name
*/
public void setName(String name)
{
m_name = name;
}
/**
* Get the roles
*
* @return the roles
*/
public List<String> getRoles()
{
return m_roles;
}
/**
* Set the roles
*
* @param roles the roles
*/
public void setRoles(List<String> roles)
{
m_roles = roles;
}
/**
* Get the password
*
* @return the password
*/
public String getPassword()
{
return m_password;
}
/**
* Set the password
*
* @param password the password
*/
public void setPassword(String password)
{
m_password = password;
}
/**
* Get the All Roles List
*
* @return the allRolesList
*/
public List getAllRolesList()
{
return m_allRolesList;
}
/**
* Set the allRolesList
*
* @param allRolesList the allRolesList
*/
public void setAllRolesList(List<SelectItem> allRolesList)
{
m_allRolesList = allRolesList;
}
/**
* Get the dateJoined
*
* @return the dateJoined
*/
public Date getDateJoined()
{
return m_dateJoined;
}
/**
* Set the dateJoined
*
* @param dateJoined the dateJoined
*/
public void setDateJoined(Date dateJoined)
{
m_dateJoined = dateJoined;
}
/** the allRolesList enum list */
private static List<SelectItem> m_allRolesList = new ArrayList<SelectItem>(8);
static
{
m_allRolesList.add(new SelectItem("admin", "Administrator"));
m_allRolesList.add(new SelectItem("superuser", "Super User"));
m_allRolesList.add(new SelectItem("dev", "Developer"));
m_allRolesList.add(new SelectItem("qa", "QA"));
m_allRolesList.add(new SelectItem("standard", "Basic User"));
}
/** the password */
private String m_password;
/** the username */
private String m_username;
/** the name */
private String m_name;
/** the roles */
private List<String> m_roles;
/** the date joined */
private Date m_dateJoined;
}

View File

@@ -0,0 +1,302 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIParameter;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.validator.ValidatorException;
import org.apache.log4j.Logger;
/**
* JSF Managed Bean. Provides the backing for the userlist.jsp view. The view uses
* the datatable control to bind to the List of User bean objects. Implements the
* action events called by the view when the user clicks the Edit link or Add button.
*
* @author kevinr
*/
public class UserListBean
{
// ===========================================================================
// Construction
public UserListBean()
{
Calendar date = new GregorianCalendar(2002, 5, 10);
m_users.add(new User("admin", "admin", "Administrator", new String[] {"admin","superuser"}, date.getTime()));
date = new GregorianCalendar(2001, 7, 10);
m_users.add(new User("kevinr", "kevinr", "Kevin Roast", new String[] {"admin","superuser","dev"}, date.getTime()));
date = new GregorianCalendar(2003, 8, 15);
m_users.add(new User("gavinc", "gavinc", "Gavin Cornwell", new String[] {"superuser","dev"}, date.getTime()));
date = new GregorianCalendar(2003, 1, 1);
m_users.add(new User("stever", "stever", "Steve Rigby", new String[] {"superuser","qa"}, date.getTime()));
}
// ===========================================================================
// Bean methods
public List getUsers()
{
return m_users;
}
public void setUsers(List<User> users)
{
m_users = users;
}
/**
* Get the users list as a wrapped DataModel object
*
* @return DataModel for use by the data-table components
*/
public DataModel getUsersModel()
{
if (m_usersModel == null)
{
m_usersModel = new ListDataModel();
m_usersModel.setWrappedData(m_users);
}
return m_usersModel;
}
public User getUser()
{
return m_currentUser;
}
public void setUser(User user)
{
m_currentUser = user;
}
/**
* Get the isNewUser
*
* @return the isNewUser
*/
public boolean getIsNewUser()
{
return m_isNewUser;
}
/**
* Set the isNewUser
*
* @param isNewUser the isNewUser
*/
public void setIsNewUser(boolean isNewUser)
{
m_isNewUser = isNewUser;
}
/**
* Get the rolesOutputText
*
* @return the rolesOutputText
*/
public HtmlOutputText getRolesOutputText()
{
return m_rolesOutputText;
}
/**
* Set the rolesOutputText
*
* @param rolesOutputText the rolesOutputText component
*/
public void setRolesOutputText(HtmlOutputText rolesOutputText)
{
m_rolesOutputText = rolesOutputText;
}
// ===========================================================================
// Action event methods
/**
* Edit user action event listener
*
* Specified directly on the appropriate tag such as commandLink or commandButton
* e.g. actionListener="#{UserListBean.editUser}"
*
* This listener cannot directly affect the navigation of the page - the command
* tag has an "action" attribute of which the default handler will use the outcome
* from the faces-config.xml by default or call a specifid controller method
* returning the String outcome as usual.
*/
public void editUser(ActionEvent event)
{
s_logger.debug("*****USERLIST: " + ((UIParameter)event.getComponent().findComponent("userId")).getValue().toString());
// Get the username from the "param" tag component we added as a nested tag
// to the command tag that fired this event.
// So we can have a key to work out which item was clicked in the data table
String usernameId = ((UIParameter)event.getComponent().findComponent("userId")).getValue().toString();
// It is also possible to get the relevent row from the DataModel we created
// wrapping our users list. But this is a weak solution as models which then
// potentially sort or page data may not provide the correct row index.
// e.g.
// m_usersModel.getWrappedData().get(m_usersModel.getRowIndex());
for (Iterator i=m_users.iterator(); i.hasNext(); /**/)
{
User user = (User)i.next();
if (user.getUsername().equals(usernameId))
{
// set the user as current so we know which one to edit etc.
try
{
setUser((User)user.clone());
setIsNewUser(false);
}
catch (CloneNotSupportedException e)
{
// will not happen - clone is supported for our own types
}
}
}
}
/**
* OK button action handler
*
* @return outcome view name
*/
public void editUserOK(ActionEvent event)
{
s_logger.debug("*****USERLIST: persisting user: " + getUser().getUsername());
for (int i=0; i<m_users.size(); i++)
{
User user = (User)m_users.get(i);
if (user.getUsername().equals(getUser().getUsername()))
{
// found modified user - persist changes
m_users.set(i, getUser());
m_usersModel = null;
break;
}
}
}
/**
* Add user action event listener
*/
public void addUser(ActionEvent event)
{
// create a blank user template
setUser(new User());
setIsNewUser(true);
}
/**
* OK button action handler
*
* @return outcome view name
*/
public void addUserOK(ActionEvent event)
{
// persist new user details
s_logger.debug("*****USERLIST: creating user: " + getUser().getUsername());
m_users.add(getUser());
m_usersModel = null;
}
/**
* Example of a value changed event handler
* NOTE: Value changed events do not submit the form directly, either a command
* button or link submits the form or can be done manually with Javascript
*/
public void roleValueChanged(ValueChangeEvent event)
{
s_logger.debug("*****USERLIST: Value change from: " + event.getOldValue() + " to: " + event.getNewValue());
// example of the use of a direct component binding
// in the JSP page, a outputText tag has used binding='beanmethod' so we
// can now programatically modify the component as required
if (m_rolesOutputText != null)
{
m_rolesOutputText.setValue(getUser().getRoles().toString());
}
// An alternative to using the component binding would be to lookup the
// component via it's component Id:
// HtmlOutputText comp = (HtmlOutputText)event.getComponent().findComponent("roles-text");
// comp.setValue(...);
// The attributes of a component are all stored in a Map, the Map features
// attribute-property transparency which means typed attributes can be get/set
// directly without using casts as the appropriate getters/setters will be
// called for you by the framework.
// comp.getAttributes().put("style", "color:red");
}
// ===========================================================================
// Validator methods
/**
* Example of a specific validation method. Required as the basic validator
* child tags are not sufficient for anything beyond very simple length checks etc.
*/
public void validateUsername(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
String username = (String)value;
if (username.length() < 5 || username.length() > 12)
{
String err = "Username must be between 5 and 12 characters in length.";
throw new ValidatorException(new FacesMessage(err));
}
else if (username.indexOf(' ') != -1 || username.indexOf('\t') != -1)
{
String err = "Username cannot contain space or whitespace characters.";
throw new ValidatorException(new FacesMessage(err));
}
}
// ===========================================================================
// Private data
private List<User> m_users = new ArrayList<User>();
private DataModel m_usersModel = null;
private User m_currentUser = null;
private boolean m_isNewUser = false;
/** the HTMLOutputText component */
private HtmlOutputText m_rolesOutputText = null;
protected final static Logger s_logger = Logger.getLogger(UserListBean.class);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest;
/**
* Managed bean that provides action handlers to navigate around the component zoo
*
* @author gavinc
*/
public class ZooService
{
public String showPropertyZoo()
{
return "showPropertyZoo";
}
public String showPropertyZoo2()
{
return "showPropertyZoo2";
}
}

View File

@@ -0,0 +1,256 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest.repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Class to represent a basic data dictionary service
*
* @author gavinc
*/
public class DataDictionary
{
private Map types;
public DataDictionary()
{
this.types = new HashMap();
// setup the dictionary
Property name = new Property("name", "string", "Name", false);
Property desc = new Property("description", "string", "Description" , false);
Property created = new Property("created", "datetime", "Created Date", true);
Property modified = new Property("modified", "datetime", "Modified Date", false);
Property keywords = new Property("keywords", "string[]", "Keywords", false);
MetaData base = new MetaData("base");
base.addProperty(name);
base.addProperty(desc);
base.addProperty(created);
base.addProperty(modified);
base.addProperty(keywords);
Property sopid = new Property("sopId", "string", "SOP ID", true);
Property effective = new Property("effective", "datetime", "Effective Date", false);
Property approved = new Property("approved", "boolean", "Approved", false);
Property latestVersion = new Property("latestversion", "string", "Latest Version", true);
MetaData sop = new MetaData("SOP");
sop.setProperties(base.getProperties());
sop.addProperty(sopid);
sop.addProperty(effective);
sop.addProperty(approved);
// add an aspect and the associated property
sop.addAspect("versionable");
sop.addProperty(latestVersion);
this.types.put(base.getTypeName(), base);
this.types.put(sop.getTypeName(), sop);
}
public MetaData getMetaData(String type)
{
return (MetaData)this.types.get(type);
}
/**
* @return Returns the types.
*/
public Map getTypes()
{
return this.types;
}
// *********************
// *** Inner classes ***
// *********************
/**
* Represents the meta data of an object
* @author gavinc
*/
public class MetaData
{
private Map propertiesMap;
private List properties;
private String typeName;
private List aspects;
public MetaData(String typeName)
{
this.properties = new ArrayList();
this.propertiesMap = new HashMap();
this.aspects = new ArrayList();
this.typeName = typeName;
}
/**
* Adds a property to the meta data object
*
* @author gavinc
*/
public void addProperty(Property property)
{
this.properties.add(property);
this.propertiesMap.put(property.getName(), property);
}
/**
* @return Returns the properties.
*/
public List getProperties()
{
return this.properties;
}
/**
* @param properties The properties to set.
*/
public void setProperties(List properties)
{
this.properties.clear();
this.propertiesMap.clear();
Iterator iter = properties.iterator();
while (iter.hasNext())
{
Property prop = (Property)iter.next();
this.properties.add(prop);
this.propertiesMap.put(prop.getName(), prop);
}
}
public Map getPropertiesMap()
{
return this.propertiesMap;
}
public List getAspects()
{
return this.aspects;
}
public void addAspect(String aspect)
{
this.aspects.add(aspect);
}
/**
* @return Returns the typeName.
*/
public String getTypeName()
{
return this.typeName;
}
}
/**
* Represents a property on an object
* @author gavinc
*/
public class Property
{
private String name;
private String type;
private String displayName;
private boolean readOnly;
/**
* @param name
* @param type
* @param readOnly
*/
public Property(String name, String type, String displayName, boolean readOnly)
{
this.name = name;
this.type = type;
this.displayName = displayName;
this.readOnly = readOnly;
}
/**
* @return Returns the name.
*/
public String getName()
{
return this.name;
}
/**
* @param name The name to set.
*/
public void setName(String name)
{
this.name = name;
}
/**
* @return Returns the type.
*/
public String getType()
{
return this.type;
}
/**
* @param type The type to set.
*/
public void setType(String type)
{
this.type = type;
}
/**
* @return Returns the displayName.
*/
public String getDisplayName()
{
return this.displayName;
}
/**
* @param displayName The displayName to set.
*/
public void setDisplayName(String displayName)
{
this.displayName = displayName;
}
/**
* @return Returns the readOnly.
*/
public boolean isReadOnly()
{
return this.readOnly;
}
/**
* @param readOnly The readOnly to set.
*/
public void setReadOnly(boolean readOnly)
{
this.readOnly = readOnly;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest.repository;
import java.io.Serializable;
/**
* Mock NodeRef object that comes from the Mock NodeService API.
*
* @author gavinc
*/
public class NodeRef implements Serializable
{
private static final long serialVersionUID = 3833183614468175153L;
private String id;
public NodeRef(String id)
{
this.id = id;
}
/**
* @return Returns the id.
*/
public String getId()
{
return id;
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package jsftest.repository;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Mock NodeService API
*
* @author gavinc
*/
public class NodeService
{
private static Log logger = LogFactory.getLog(NodeService.class);
public static NodeRef getNodeRef(String path)
{
return new NodeRef(path);
}
public static String getType(NodeRef nodeRef)
{
String id = nodeRef.getId();
String type = null;
if (id.equalsIgnoreCase("/gav.doc") ||
id.equalsIgnoreCase("/kev.txt"))
{
type = "base";
}
else if (id.equalsIgnoreCase("/sop.txt"))
{
type = "SOP";
}
return type;
}
public static Map getProperties(NodeRef nodeRef)
{
String id = nodeRef.getId();
Map properties = null;
if (id.equalsIgnoreCase("/gav.doc"))
{
properties = createProperties("Gav", "Gavs Object",
new String[] {"gav", "gadget", "gibbon"}, null);
}
else if (id.equalsIgnoreCase("/kev.txt"))
{
properties = createProperties("Kev", "Kevs Object",
new String[] {"kev", "monkey"}, null);
}
else if (id.equalsIgnoreCase("/sop.txt"))
{
properties = createProperties("SOP", "A manufacturing SOP",
new String[] {"sop", "manufacturing"}, "sop1");
}
return properties;
}
private static Map createProperties(String name, String desc,
String[] keywords, String sop)
{
HashMap props = new HashMap();
Date date = new Date();
props.put("name", name);
props.put("description", desc);
props.put("keywords", keywords);
props.put("created", date);
props.put("modified", date);
if (sop != null)
{
props.put("sopId", sop);
props.put("effective", date);
props.put("approved", new Boolean(true));
props.put("latestversion", "1.6");
}
return props;
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app;
import javax.faces.application.NavigationHandler;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigService;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.config.NavigationConfigElement;
import org.alfresco.web.config.NavigationElementReader;
import org.alfresco.web.config.NavigationResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.jsf.FacesContextUtils;
/**
* @author gavinc
*/
public class AlfrescoNavigationHandler extends NavigationHandler
{
private final static Log logger = LogFactory.getLog(AlfrescoNavigationHandler.class);
// The original navigation handler
private NavigationHandler origHandler;
/**
* Default constructor
*
* @param origHandler The original navigation handler
*/
public AlfrescoNavigationHandler(NavigationHandler origHandler)
{
super();
this.origHandler = origHandler;
}
/**
* @see javax.faces.application.NavigationHandler#handleNavigation(javax.faces.context.FacesContext, java.lang.String, java.lang.String)
*/
@Override
public void handleNavigation(FacesContext context, String fromAction, String outcome)
{
if (logger.isDebugEnabled())
logger.debug("handleNavigation (fromAction=" + fromAction + ", outcome=" + outcome + ")");
boolean useOriginalNavHandler = true;
String finalOutcome = outcome;
String viewId = context.getViewRoot().getViewId();
if (logger.isDebugEnabled())
logger.debug("Current view id: " + viewId);
NavigationBean navBean = (NavigationBean)context.getExternalContext().
getSessionMap().get("NavigationBean");
// only continue if we have some dispatching context
if (navBean != null && navBean.getDispatchContextNode() != null)
{
Node node = navBean.getDispatchContextNode();
if (logger.isDebugEnabled())
logger.debug("Found node in dispatch context: " + node);
// see if there is any navigation config for the node type
ConfigService configSvc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
context).getBean(Application.BEAN_CONFIG_SERVICE);
Config nodeConfig = configSvc.getConfig(node);
NavigationConfigElement navigationCfg = (NavigationConfigElement)nodeConfig.
getConfigElement(NavigationElementReader.ELEMENT_NAVIGATION);
if (navigationCfg != null)
{
// see if there is config for the current view state
NavigationResult navResult = navigationCfg.getOverride(viewId, outcome);
if (navResult != null)
{
if (logger.isDebugEnabled())
logger.debug("Found navigation config: " + navResult);
if (navResult.isOutcome())
{
finalOutcome = navResult.getResult();
}
else
{
String newViewId = navResult.getResult();
if (newViewId.equals(viewId) == false)
{
useOriginalNavHandler = false;
if (logger.isDebugEnabled())
logger.debug("Dispatching to new view id: " + newViewId);
ViewHandler viewHandler = context.getApplication().getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(context, newViewId);
viewRoot.setViewId(newViewId);
context.setViewRoot(viewRoot);
context.renderResponse();
}
else
{
if (logger.isDebugEnabled())
logger.debug("New view id is the same as the current one so setting outcome to null");
finalOutcome = null;
}
}
}
else if (logger.isDebugEnabled())
{
logger.debug("No override configuration found for current view or outcome");
}
}
else if (logger.isDebugEnabled())
{
logger.debug("No navigation configuration found for node");
}
// reset the dispatch context
navBean.resetDispatchContext();
}
else if (logger.isDebugEnabled())
{
logger.debug("No dispatch context found");
}
// do the appropriate navigation handling
if (useOriginalNavHandler)
{
if (logger.isDebugEnabled())
logger.debug("Passing outcome '" + finalOutcome + "' to original navigation handler");
this.origHandler.handleNavigation(context, fromAction, finalOutcome);
}
}
}

View File

@@ -0,0 +1,662 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import javax.faces.context.FacesContext;
import javax.portlet.PortletContext;
import javax.portlet.PortletSession;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.alfresco.config.ConfigService;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.bean.ErrorBean;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.config.ServerConfigElement;
import org.apache.commons.logging.Log;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Utilities class
*
* @author gavinc
*/
public class Application
{
private static final String LOCALE = "locale";
public static final String BEAN_CONFIG_SERVICE = "configService";
public static final String BEAN_DATA_DICTIONARY = "dataDictionary";
public static final String BEAN_IMPORTER_BOOTSTRAP = "importerBootstrap";
public static final String MESSAGE_BUNDLE = "alfresco.messages.webclient";
private static boolean inPortalServer = true;
private static StoreRef repoStoreRef;
private static String rootPath;
private static String companyRootId;
private static String companyRootDescription;
private static String glossaryFolderName;
private static String spaceTemplatesFolderName;
private static String contentTemplatesFolderName;
/**
* Private constructor to prevent instantiation of this class
*/
private Application()
{
}
/**
* Sets whether this application is running inside a portal server
*
* @param inPortal true to indicate the application is running as a portlet
*/
public static void setInPortalServer(boolean inPortal)
{
inPortalServer = inPortal;
}
/**
* Determines whether the server is running in a portal
*
* @return true if we are running inside a portal server
*/
public static boolean inPortalServer()
{
return inPortalServer;
}
/**
* Handles errors thrown from servlets
*
* @param servletContext The servlet context
* @param request The HTTP request
* @param response The HTTP response
* @param error The exception
* @param logger The logger
*/
public static void handleServletError(ServletContext servletContext, HttpServletRequest request,
HttpServletResponse response, Throwable error, Log logger, String returnPage)
throws IOException, ServletException
{
// get the error bean from the session and set the error that occurred.
HttpSession session = request.getSession();
ErrorBean errorBean = (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME);
if (errorBean == null)
{
errorBean = new ErrorBean();
session.setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean);
}
errorBean.setLastError(error);
errorBean.setReturnPage(returnPage);
// try and find the configured error page
boolean errorShown = false;
String errorPage = getErrorPage(servletContext);
if (errorPage != null)
{
if (logger.isDebugEnabled())
logger.debug("An error has occurred, redirecting to error page: " + errorPage);
if (response.isCommitted() == false)
{
errorShown = true;
response.sendRedirect(request.getContextPath() + errorPage);
}
else
{
if (logger.isDebugEnabled())
logger.debug("Response is already committed, re-throwing error");
}
}
else
{
if (logger.isDebugEnabled())
logger.debug("No error page defined, re-throwing error");
}
// if we could not show the error page for whatever reason, re-throw the error
if (!errorShown)
{
if (error instanceof IOException)
{
throw (IOException)error;
}
else if (error instanceof ServletException)
{
throw (ServletException)error;
}
else
{
throw new ServletException(error);
}
}
}
/**
* Retrieves the configured error page for the application
*
* @param servletContext The servlet context
* @return The configured error page or null if the configuration is missing
*/
public static String getErrorPage(ServletContext servletContext)
{
return getErrorPage(WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext));
}
/**
* Retrieves the configured error page for the application
*
* @param portletContext The portlet context
* @return
*/
public static String getErrorPage(PortletContext portletContext)
{
return getErrorPage((WebApplicationContext)portletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
}
/**
* Retrieves the configured login page for the application
*
* @param servletContext The servlet context
* @return The configured login page or null if the configuration is missing
*/
public static String getLoginPage(ServletContext servletContext)
{
return getLoginPage(WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext));
}
/**
* Retrieves the configured login page for the application
*
* @param portletContext The portlet context
* @return
*/
public static String getLoginPage(PortletContext portletContext)
{
return getLoginPage((WebApplicationContext)portletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE));
}
/**
* @return Returns the User object representing the currently logged in user
*/
public static User getCurrentUser(HttpSession session)
{
return (User)session.getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
}
/**
* @return Returns the User object representing the currently logged in user
*/
public static User getCurrentUser(FacesContext context)
{
return (User)context.getExternalContext().getSessionMap().get(AuthenticationHelper.AUTHENTICATION_USER);
}
/**
* @return Returns the repository store URL (retrieved from config service)
*/
public static StoreRef getRepositoryStoreRef(ServletContext context)
{
return getRepositoryStoreRef(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the repository store URL (retrieved from config service)
*/
public static StoreRef getRepositoryStoreRef(FacesContext context)
{
return getRepositoryStoreRef(FacesContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns id of the company root
*/
public static String getCompanyRootId()
{
return companyRootId;
}
/**
* Sets the company root id. This is setup by the ContextListener.
*
* @param id The company root id
*/
public static void setCompanyRootId(String id)
{
companyRootId = id;
}
/**
* @return Returns the root path for the application (retrieved from config service)
*/
public static String getRootPath(ServletContext context)
{
return getRootPath(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the root path for the application (retrieved from config service)
*/
public static String getRootPath(FacesContext context)
{
return getRootPath(FacesContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the glossary folder name (retrieved from config service)
*/
public static String getGlossaryFolderName(ServletContext context)
{
return getGlossaryFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the glossary folder name (retrieved from config service)
*/
public static String getGlossaryFolderName(FacesContext context)
{
return getGlossaryFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the Space templates folder name (retrieved from config service)
*/
public static String getSpaceTemplatesFolderName(ServletContext context)
{
return getSpaceTemplatesFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the Space templates folder name (retrieved from config service)
*/
public static String getSpaceTemplatesFolderName(FacesContext context)
{
return getSpaceTemplatesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the Content templates folder name (retrieved from config service)
*/
public static String getContentTemplatesFolderName(ServletContext context)
{
return getContentTemplatesFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Returns the Content templates folder name (retrieved from config service)
*/
public static String getContentTemplatesFolderName(FacesContext context)
{
return getContentTemplatesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
}
/**
* Set the language locale for the current user context
*
* @param context FacesContext for current user
* @param code The ISO locale code to set
*/
public static void setLanguage(FacesContext context, String code)
{
Locale locale = parseLocale(code);
// set locale for JSF framework usage
context.getViewRoot().setLocale(locale);
// set locale for our framework usage
context.getExternalContext().getSessionMap().put(LOCALE, locale);
// clear the current message bundle - so it's reloaded with new locale
context.getExternalContext().getSessionMap().remove(MESSAGE_BUNDLE);
}
/**
* Set the language locale for the current user session
*
* @param session HttpSession for current user
* @param code The ISO locale code to set
*/
public static void setLanguage(HttpSession session, String code)
{
Locale locale = parseLocale(code);
session.putValue(LOCALE, locale);
session.removeAttribute(MESSAGE_BUNDLE);
}
/**
* @param code Locale code (java format with underscores) to parse
* @return Locale object or default if unable to parse
*/
private static Locale parseLocale(String code)
{
Locale locale = Locale.getDefault();
StringTokenizer t = new StringTokenizer(code, "_");
int tokens = t.countTokens();
if (tokens == 1)
{
locale = new Locale(code);
}
else if (tokens == 2)
{
locale = new Locale(t.nextToken(), t.nextToken());
}
else if (tokens == 3)
{
locale = new Locale(t.nextToken(), t.nextToken(), t.nextToken());
}
return locale;
}
/**
* Return the language Locale for the current user context
*
* @param context FacesContext for the current user
*
* @return Current language Locale set or null if none set
*/
public static Locale getLanguage(FacesContext context)
{
return (Locale)context.getExternalContext().getSessionMap().get(LOCALE);
}
/**
* Return the language Locale for the current user Session.
*
* @param session HttpSession for the current user
*
* @return Current language Locale set or null if none set
*/
public static Locale getLanguage(HttpSession session)
{
return (Locale)session.getAttribute(LOCALE);
}
/**
* Return the language Locale for the current user PortletSession.
*
* @param session PortletSession for the current user
*
* @return Current language Locale set or null if none set
*/
public static Locale getLanguage(PortletSession session)
{
return (Locale)session.getAttribute(LOCALE);
}
/**
* Get the specified I18N message string from the default message bundle for this user
*
* @param context FacesContext
* @param msg Message ID
*
* @return String from message bundle or $$msg$$ if not found
*/
public static String getMessage(FacesContext context, String msg)
{
return getBundle(context).getString(msg);
}
/**
* Get the specified I18N message string from the default message bundle for this user
*
* @param session HttpSession
* @param msg Message ID
*
* @return String from message bundle or $$msg$$ if not found
*/
public static String getMessage(HttpSession session, String msg)
{
return getBundle(session).getString(msg);
}
/**
* Get the specified the default message bundle for this user
*
* @param session HttpSession
*
* @return ResourceBundle for this user
*/
public static ResourceBundle getBundle(HttpSession session)
{
ResourceBundle bundle = (ResourceBundle)session.getAttribute(MESSAGE_BUNDLE);
if (bundle == null)
{
// get Locale from language selected by each user on login
Locale locale = (Locale)session.getAttribute(LOCALE);
if (locale == null)
{
locale = Locale.getDefault();
}
bundle = ResourceBundle.getBundle(MESSAGE_BUNDLE, locale);
if (bundle == null)
{
throw new AlfrescoRuntimeException("Unable to load Alfresco messages bundle: " + MESSAGE_BUNDLE);
}
// apply our wrapper to catch MissingResourceException
bundle = new ResourceBundleWrapper(bundle);
session.setAttribute(MESSAGE_BUNDLE, bundle);
}
return bundle;
}
/**
* Get the specified the default message bundle for this user
*
* @param context FacesContext
*
* @return ResourceBundle for this user
*/
public static ResourceBundle getBundle(FacesContext context)
{
// get the resource bundle for the current locale
// we store the bundle in the users session
// this makes it easy to add a locale per user support later
Map session = context.getExternalContext().getSessionMap();
ResourceBundle bundle = (ResourceBundle)session.get(MESSAGE_BUNDLE);
if (bundle == null)
{
// get Locale from language selected by each user on login
Locale locale = (Locale)session.get(LOCALE);
if (locale == null)
{
locale = Locale.getDefault();
}
bundle = ResourceBundle.getBundle(MESSAGE_BUNDLE, locale);
if (bundle == null)
{
throw new AlfrescoRuntimeException("Unable to load Alfresco messages bundle: " + MESSAGE_BUNDLE);
}
// apply our wrapper to catch MissingResourceException
bundle = new ResourceBundleWrapper(bundle);
session.put(MESSAGE_BUNDLE, bundle);
}
return bundle;
}
/**
* Helper to get the ConfigService instance
*
* @param context FacesContext
*
* @return ConfigService
*/
public static ConfigService getConfigService(FacesContext context)
{
return (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(context).getBean(
Application.BEAN_CONFIG_SERVICE);
}
/**
* Returns the repository store URL (retrieved from config service)
*
* @param context The spring context
* @return The repository store URL to use
*/
private static StoreRef getRepositoryStoreRef(WebApplicationContext context)
{
if (repoStoreRef == null)
{
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
repoStoreRef = bootstrap.getStoreRef();
}
return repoStoreRef;
}
/**
* Returns the root path for the application (retrieved from config service)
*
* @param context The spring context
* @return The application root path
*/
private static String getRootPath(WebApplicationContext context)
{
if (rootPath == null)
{
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
Properties configuration = bootstrap.getConfiguration();
rootPath = configuration.getProperty("spaces.company_home.childname");
}
return rootPath;
}
/**
* Returns the glossary folder name (retrieved from config service)
*
* @param context The spring context
* @return The glossary folder name
*/
private static String getGlossaryFolderName(WebApplicationContext context)
{
if (glossaryFolderName == null)
{
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
Properties configuration = bootstrap.getConfiguration();
glossaryFolderName = configuration.getProperty("spaces.dictionary.childname");
}
return glossaryFolderName;
}
/**
* Returns the Space Templates folder name (retrieved from config service)
*
* @param context The spring context
* @return The templates folder name
*/
private static String getSpaceTemplatesFolderName(WebApplicationContext context)
{
if (spaceTemplatesFolderName == null)
{
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
Properties configuration = bootstrap.getConfiguration();
spaceTemplatesFolderName = configuration.getProperty("spaces.templates.childname");
}
return spaceTemplatesFolderName;
}
/**
* Returns the Content Templates folder name (retrieved from config service)
*
* @param context The spring context
* @return The templates folder name
*/
private static String getContentTemplatesFolderName(WebApplicationContext context)
{
if (contentTemplatesFolderName == null)
{
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
Properties configuration = bootstrap.getConfiguration();
contentTemplatesFolderName = configuration.getProperty("spaces.templates.content.childname");
}
return contentTemplatesFolderName;
}
/**
* Retrieves the configured error page for the application
*
* @param context The Spring contexr
* @return The configured error page or null if the configuration is missing
*/
private static String getErrorPage(WebApplicationContext context)
{
String errorPage = null;
ConfigService svc = (ConfigService)context.getBean(BEAN_CONFIG_SERVICE);
ServerConfigElement serverConfig = (ServerConfigElement)svc.getGlobalConfig().getConfigElement("server");
if (serverConfig != null)
{
errorPage = serverConfig.getErrorPage();
}
return errorPage;
}
/**
* Retrieves the configured login page for the application
*
* @param context The Spring contexr
* @return The configured login page or null if the configuration is missing
*/
private static String getLoginPage(WebApplicationContext context)
{
String loginPage = null;
ConfigService svc = (ConfigService)context.getBean(BEAN_CONFIG_SERVICE);
ServerConfigElement serverConfig = (ServerConfigElement)svc.getGlobalConfig().getConfigElement("server");
if (serverConfig != null)
{
loginPage = serverConfig.getLoginPage();
}
return loginPage;
}
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.transaction.UserTransaction;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigService;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.web.app.portlet.AlfrescoFacesPortlet;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* ServletContextListener implementation that initialises the application.
*
* NOTE: This class must appear after the Spring context loader listener
*
* @author gavinc
*/
public class ContextListener implements ServletContextListener, HttpSessionListener
{
private static Log logger = LogFactory.getLog(ContextListener.class);
private static final String ADMIN = "admin";
private static final String ADMIN_FIRSTNAME = "Repository";
private static final String ADMIN_LASTNAME = "Administrator";
private ServletContext servletContext;
/**
* @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent event)
{
// make sure that the spaces store in the repository exists
this.servletContext = event.getServletContext();
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
ServiceRegistry registry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
TransactionService transactionService = registry.getTransactionService();
NodeService nodeService = registry.getNodeService();
SearchService searchService = registry.getSearchService();
NamespaceService namespaceService = registry.getNamespaceService();
AuthenticationComponent authenticationComponent = (AuthenticationComponent) ctx
.getBean("authenticationComponent");
// repo bootstrap code for our client
UserTransaction tx = null;
NodeRef companySpaceNodeRef = null;
try
{
tx = transactionService.getUserTransaction();
tx.begin();
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
// get and setup the initial store ref from config
StoreRef storeRef = Repository.getStoreRef(servletContext);
// check the repository exists, create if it doesn't
if (nodeService.exists(storeRef) == false)
{
throw new AlfrescoRuntimeException("Store not created prior to application startup: " + storeRef);
}
// get hold of the root node
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
// see if the company home space is present
String rootPath = Application.getRootPath(servletContext);
if (rootPath == null)
{
throw new AlfrescoRuntimeException("Root path has not been configured");
}
List<NodeRef> nodes = searchService.selectNodes(rootNodeRef, rootPath, null, namespaceService, false);
if (nodes.size() == 0)
{
throw new AlfrescoRuntimeException("Root path not created prior to application startup: " + rootPath);
}
// Extract company space id and store it in the Application object
companySpaceNodeRef = nodes.get(0);
Application.setCompanyRootId(companySpaceNodeRef.getId());
// check the admin user exists, create if it doesn't
MutableAuthenticationDao dao = (MutableAuthenticationDao) ctx.getBean("alfDaoImpl");
// this is required to setup the ACEGI context before we can check
// for the user
if (!dao.userExists(ADMIN))
{
ConfigService configService = (ConfigService) ctx.getBean(Application.BEAN_CONFIG_SERVICE);
// default to password of "admin" if we don't find config for it
String password = ADMIN;
ConfigElement adminConfig = configService.getGlobalConfig().getConfigElement("admin");
if (adminConfig != null)
{
List<ConfigElement> children = adminConfig.getChildren();
if (children.size() != 0)
{
// try to find the config element for the initial
// password
ConfigElement passElement = children.get(0);
if (passElement.getName().equals("initial-password"))
{
password = passElement.getValue();
}
}
}
// create the Authentication instance for the "admin" user
AuthenticationService authService = (AuthenticationService) ctx.getBean("authenticationService");
authService.createAuthentication(ADMIN, password.toCharArray());
}
PersonService personService = (PersonService) ctx.getBean("personService");
if (!personService.personExists(ADMIN))
{
// create the node to represent the Person instance for the
// admin user
Map<QName, Serializable> props = new HashMap<QName, Serializable>(7, 1.0f);
props.put(ContentModel.PROP_USERNAME, ADMIN);
props.put(ContentModel.PROP_FIRSTNAME, ADMIN_FIRSTNAME);
props.put(ContentModel.PROP_LASTNAME, ADMIN_LASTNAME);
props.put(ContentModel.PROP_HOMEFOLDER, companySpaceNodeRef);
props.put(ContentModel.PROP_EMAIL, "");
props.put(ContentModel.PROP_ORGID, "");
personService.createPerson(props);
}
// set the store's GUEST access if we are allowed to modify permissions
if (!transactionService.isReadOnly())
{
PermissionService permissionService = (PermissionService) ctx.getBean("permissionService");
permissionService.setPermission(rootNodeRef, permissionService.getAllAuthorities(), PermissionService.GUEST, true);
}
// commit the transaction
tx.commit();
}
catch (Throwable e)
{
// rollback the transaction
try
{
if (tx != null)
{
tx.rollback();
}
}
catch (Exception ex) {}
logger.error("Failed to initialise ", e);
throw new AlfrescoRuntimeException("Failed to initialise ", e);
}
finally
{
try
{
authenticationComponent.clearCurrentSecurityContext();
}
catch (Exception ex) {}
}
}
/**
* @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent event)
{
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
Scheduler quartz = (Scheduler) ctx.getBean("schedulerFactory");
try
{
quartz.shutdown(true);
}
catch (SchedulerException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Session created listener
*/
public void sessionCreated(HttpSessionEvent event)
{
if (logger.isDebugEnabled()) logger.debug("HTTP session created: " + event.getSession().getId());
}
/**
* Session destroyed listener
*/
public void sessionDestroyed(HttpSessionEvent event)
{
if (logger.isDebugEnabled()) logger.debug("HTTP session destroyed: " + event.getSession().getId());
User user;
if (Application.inPortalServer() == false)
{
user = (User)event.getSession().getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
}
else
{
user = (User)event.getSession().getAttribute(AlfrescoFacesPortlet.MANAGED_BEAN_PREFIX + AuthenticationHelper.AUTHENTICATION_USER);
}
if (user != null)
{
// invalidate ticket
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
AuthenticationService authService = (AuthenticationService) ctx.getBean("authenticationService");
authService.invalidateTicket(user.getTicket());
event.getSession().removeAttribute(AuthenticationHelper.AUTHENTICATION_USER);
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Debug phase listener that simply logs when each phase is entered and exited.
*
* @author gavinc
*/
public class DebugPhaseListener implements PhaseListener
{
private static final Log logger = LogFactory.getLog(DebugPhaseListener.class);
/**
* @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
*/
public void afterPhase(PhaseEvent event)
{
if (logger.isDebugEnabled())
logger.debug("********** Exiting phase: " + event.getPhaseId().toString());
}
/**
* @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
*/
public void beforePhase(PhaseEvent event)
{
if (logger.isDebugEnabled())
logger.debug("********** Entering phase: " + event.getPhaseId().toString());
}
/**
* @see javax.faces.event.PhaseListener#getPhaseId()
*/
public PhaseId getPhaseId()
{
return PhaseId.ANY_PHASE;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app;
import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
/**
* Wrapper around Alfresco Resource Bundle objects. Used to catch and handle missing
* resource exception to help identify missing I18N strings in client apps.
*
* @author Kevin Roast
*/
public final class ResourceBundleWrapper extends ResourceBundle
{
private static Logger logger = Logger.getLogger(ResourceBundleWrapper.class);
private ResourceBundle delegate;
/**
* Constructor
*
* @param bundle The ResourceBundle to route calls too
*/
public ResourceBundleWrapper(ResourceBundle bundle)
{
this.delegate = bundle;
}
/**
* @see java.util.ResourceBundle#getKeys()
*/
public Enumeration<String> getKeys()
{
return this.delegate.getKeys();
}
/**
* @see java.util.ResourceBundle#handleGetObject(java.lang.String)
*/
protected Object handleGetObject(String key)
{
try
{
return this.delegate.getObject(key);
}
catch (MissingResourceException err)
{
if (logger.isEnabledFor(Priority.WARN))
logger.warn("Failed to find I18N message string key: " + key);
return "$$" + key + "$$";
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.context;
/**
* Interface used to allow Beans to register themselves as interested in UI context events.
* <p>
* Beans supporting this interface should be register against the UIContextService. Then Beans
* which wish to indicate that the UI should refresh itself i.e. dump all cached data and settings,
* call the UIContextService.notifyBeans() to inform all registered instances of the change.
*
* @author Kevin Roast
*/
public interface IContextListener
{
/**
* Method called by UIContextService.notifyBeans() to inform all registered beans that
* all UI Beans should refresh dump all cached data and settings.
*/
public void contextUpdated();
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.context;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
/**
* Beans supporting the IContextListener interface are registered against this class. Then Beans
* which wish to indicate that the UI should refresh itself i.e. dump all cached data and settings,
* call the UIContextService.notifyBeans() to inform all registered instances of the change.
*
* @author Kevin Roast
*/
public final class UIContextService
{
/**
* Private constructor
*/
private UIContextService()
{
}
/**
* Returns a Session local instance of the UIContextService
*
* @return UIContextService for this Thread
*/
public static UIContextService getInstance(FacesContext fc)
{
Map session = fc.getExternalContext().getSessionMap();
UIContextService service = (UIContextService)session.get(CONTEXT_KEY);
if (service == null)
{
service = new UIContextService();
session.put(CONTEXT_KEY, service);
}
return service;
}
/**
* Register a bean to be informed of context events
*
* @param bean Conforming to the IContextListener interface
*/
public void registerBean(IContextListener bean)
{
if (bean == null)
{
throw new IllegalArgumentException("Bean reference specified cannot be null!");
}
this.registeredBeans.put(bean.getClass(), bean);
}
/**
* Remove a bean reference from those notified of changes
*
* @param bean Conforming to the IContextListener interface
*/
public void unregisterBean(IContextListener bean)
{
if (bean == null)
{
throw new IllegalArgumentException("Bean reference specified cannot be null!");
}
this.registeredBeans.remove(bean);
}
/**
* Call to notify all register beans that the UI context has changed and they should
* refresh themselves as appropriate.
*/
public void notifyBeans()
{
for (IContextListener listener: this.registeredBeans.values())
{
listener.contextUpdated();
}
}
/** key for the UI context service in the session */
private final static String CONTEXT_KEY = "__uiContextService";
/** Map of bean registered against the context service */
private Map<Class, IContextListener> registeredBeans = new HashMap<Class, IContextListener>(7, 1.0f);
}

View File

@@ -0,0 +1,343 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.portlet;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.util.TempFileProvider;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.bean.ErrorBean;
import org.alfresco.web.bean.FileUploadBean;
import org.alfresco.web.bean.repository.User;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.portlet.PortletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.portlet.MyFacesGenericPortlet;
import org.apache.myfaces.portlet.PortletUtil;
import org.springframework.web.context.WebApplicationContext;
/**
* Class to extend the MyFacesGenericPortlet to provide behaviour specific to Alfresco web client.
* Handles upload of multi-part forms through a JSR-168 Portlet, generic error handling and session
* login authentication.
*
* @author Gavin Cornwell, Kevin Roast
*/
public class AlfrescoFacesPortlet extends MyFacesGenericPortlet
{
public static final String INSTANCE_NAME = "AlfrescoClientInstance";
public static final String WINDOW_NAME = "AlfrescoClientWindow";
public static final String MANAGED_BEAN_PREFIX = "javax.portlet.p." + INSTANCE_NAME +
"." + WINDOW_NAME + "?";
private static final String ERROR_PAGE_PARAM = "error-page";
private static final String ERROR_OCCURRED = "error-occurred";
private static Log logger = LogFactory.getLog(AlfrescoFacesPortlet.class);
private String loginPage = null;
private String errorPage = null;
/**
* Called by the portlet container to allow the portlet to process an action request.
*/
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException
{
boolean isMultipart = PortletFileUpload.isMultipartContent(request);
try
{
// NOTE: Due to filters not being called within portlets we can not make use
// of the MyFaces file upload support, therefore we are using a pure
// portlet request/action to handle file uploads until there is a
// solution.
if (isMultipart)
{
if (logger.isDebugEnabled())
logger.debug("Handling multipart request...");
PortletSession session = request.getPortletSession();
// get the file from the request and put it in the session
DiskFileItemFactory factory = new DiskFileItemFactory();
PortletFileUpload upload = new PortletFileUpload(factory);
List<FileItem> fileItems = upload.parseRequest(request);
Iterator<FileItem> iter = fileItems.iterator();
FileUploadBean bean = new FileUploadBean();
while(iter.hasNext())
{
FileItem item = iter.next();
String filename = item.getName();
if(item.isFormField() == false)
{
if (logger.isDebugEnabled())
logger.debug("Processing uploaded file: " + filename);
// workaround a bug in IE where the full path is returned
// IE is only available for Windows so only check for the Windows path separator
int idx = filename.lastIndexOf('\\');
if (idx == -1)
{
// if there is no windows path separator check for *nix
idx = filename.lastIndexOf('/');
}
if (idx != -1)
{
filename = filename.substring(idx + File.separator.length());
}
File tempFile = TempFileProvider.createTempFile("alfresco", ".upload");
item.write(tempFile);
bean.setFile(tempFile);
bean.setFileName(filename);
bean.setFilePath(tempFile.getAbsolutePath());
session.setAttribute(FileUploadBean.FILE_UPLOAD_BEAN_NAME, bean,
PortletSession.PORTLET_SCOPE);
}
}
// it doesn't matter what the value is we just need the VIEW_ID parameter
// to tell the faces portlet bridge to treat the request as a JSF request,
// this will send us back to the same page we came from, which is fine for
// most scenarios.
response.setRenderParameter(VIEW_ID, "a-jsf-page");
}
else
{
// do the normal JSF processing
super.processAction(request, response);
}
}
catch (Throwable e)
{
if (getErrorPage() != null)
{
handleError(request, response, e);
}
else
{
logger.warn("No error page configured, re-throwing exception");
if (e instanceof PortletException)
{
throw (PortletException)e;
}
else if (e instanceof IOException)
{
throw (IOException)e;
}
else
{
throw new PortletException(e);
}
}
}
}
/**
* @see org.apache.myfaces.portlet.MyFacesGenericPortlet#facesRender(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
*/
protected void facesRender(RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
Application.setInPortalServer(true);
if (request.getParameter(ERROR_OCCURRED) != null)
{
String errorPage = Application.getErrorPage(getPortletContext());
if (logger.isDebugEnabled())
logger.debug("An error has occurred, redirecting to error page: " + errorPage);
response.setContentType("text/html");
PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(errorPage);
dispatcher.include(request, response);
}
else
{
// if we have no User object in the session then a timeout must have occured
// use the viewId to check that we are not already on the login page
String viewId = request.getParameter(VIEW_ID);
User user = (User)request.getPortletSession().getAttribute(AuthenticationHelper.AUTHENTICATION_USER);
if (user == null && (viewId == null || viewId.equals(getLoginPage()) == false))
{
if (logger.isDebugEnabled())
logger.debug("No valid login, requesting login page. ViewId: " + viewId);
// login page redirect
response.setContentType("text/html");
request.getPortletSession().setAttribute(PortletUtil.PORTLET_REQUEST_FLAG, "true");
nonFacesRequest(request, response);
}
else
{
try
{
if (user != null)
{
// setup the authentication context
WebApplicationContext ctx = (WebApplicationContext)getPortletContext().getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
AuthenticationService auth = (AuthenticationService)ctx.getBean("authenticationService");
auth.validate(user.getTicket());
}
// Set the current locale
I18NUtil.setLocale(Application.getLanguage(request.getPortletSession()));
// do the normal JSF processing
super.facesRender(request, response);
}
catch (Throwable e)
{
if (getErrorPage() != null)
{
handleError(request, response, e);
}
else
{
logger.warn("No error page configured, re-throwing exception");
if (e instanceof PortletException)
{
throw (PortletException)e;
}
else if (e instanceof IOException)
{
throw (IOException)e;
}
else
{
throw new PortletException(e);
}
}
}
}
}
}
/**
* Handles errors that occur during a process action request
*/
private void handleError(ActionRequest request, ActionResponse response, Throwable error)
throws PortletException, IOException
{
// get the error bean from the session and set the error that occurred.
PortletSession session = request.getPortletSession();
ErrorBean errorBean = (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME,
PortletSession.PORTLET_SCOPE);
if (errorBean == null)
{
errorBean = new ErrorBean();
session.setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean, PortletSession.PORTLET_SCOPE);
}
errorBean.setLastError(error);
response.setRenderParameter(ERROR_OCCURRED, "true");
}
/**
* Handles errors that occur during a render request
*/
private void handleError(RenderRequest request, RenderResponse response, Throwable error)
throws PortletException, IOException
{
// get the error bean from the session and set the error that occurred.
PortletSession session = request.getPortletSession();
ErrorBean errorBean = (ErrorBean)session.getAttribute(ErrorBean.ERROR_BEAN_NAME,
PortletSession.PORTLET_SCOPE);
if (errorBean == null)
{
errorBean = new ErrorBean();
session.setAttribute(ErrorBean.ERROR_BEAN_NAME, errorBean, PortletSession.PORTLET_SCOPE);
}
errorBean.setLastError(error);
// if the faces context is available set the current view to the browse page
// so that the error page goes back to the application (rather than going back
// to the same page which just throws the error again meaning we can never leave
// the error page)
FacesContext context = FacesContext.getCurrentInstance();
if (context != null)
{
ViewHandler viewHandler = context.getApplication().getViewHandler();
// TODO: configure the portlet error return page
UIViewRoot view = viewHandler.createView(context, "/jsp/browse/browse.jsp");
context.setViewRoot(view);
}
// get the error page and include that instead
String errorPage = getErrorPage();
if (logger.isDebugEnabled())
logger.debug("An error has occurred, redirecting to error page: " + errorPage);
response.setContentType("text/html");
PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(errorPage);
dispatcher.include(request, response);
}
/**
* @return Retrieves the configured login page
*/
private String getLoginPage()
{
if (this.loginPage == null)
{
this.loginPage = Application.getLoginPage(getPortletContext());
}
return this.loginPage;
}
/**
* @return Retrieves the configured error page
*/
private String getErrorPage()
{
if (this.errorPage == null)
{
this.errorPage = Application.getErrorPage(getPortletContext());
}
return this.errorPage;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import javax.faces.webapp.FacesServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.web.app.Application;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Wrapper around standard faces servlet to provide error handling
*
* @author gavinc
*/
public class AlfrescoFacesServlet extends FacesServlet
{
private static Log logger = LogFactory.getLog(AlfrescoFacesServlet.class);
/**
* @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
*/
public void service(ServletRequest request, ServletResponse response)
throws IOException, ServletException
{
try
{
super.service(request, response);
}
catch (Throwable error)
{
String returnPage = ((HttpServletRequest)request).getRequestURI();
Application.handleServletError(getServletConfig().getServletContext(), (HttpServletRequest)request,
(HttpServletResponse)response, error, logger, returnPage);
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.LoginBean;
/**
* @author Kevin Roast
*
* Servlet filter responsible for redirecting to the login page for the Web Client if the user
* does not have a valid ticket.
* <p>
* The current ticker is validated for each page request and the login page is shown if the
* ticker has expired.
* <p>
* Note that this filter is only active when the system is running in a servlet container -
* the AlfrescoFacesPortlet will be used for a JSR-168 Portal environment.
*/
public class AuthenticationFilter implements Filter
{
/**
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig config) throws ServletException
{
this.context = config.getServletContext();
}
/**
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest httpReq = (HttpServletRequest)req;
// allow the login page to proceed
if (httpReq.getRequestURI().endsWith(getLoginPage()) == false)
{
if (AuthenticationHelper.authenticate(this.context, httpReq, (HttpServletResponse)res))
{
// continue filter chaining
chain.doFilter(req, res);
}
else
{
// failed to authenticate - save redirect URL for after login process
httpReq.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, httpReq.getRequestURI());
}
}
else
{
// continue filter chaining
chain.doFilter(req, res);
}
}
/**
* @see javax.servlet.Filter#destroy()
*/
public void destroy()
{
// nothing to do
}
/**
* @return The login page url
*/
private String getLoginPage()
{
if (this.loginPage == null)
{
this.loginPage = Application.getLoginPage(this.context);
}
return this.loginPage;
}
private String loginPage = null;
private ServletContext context;
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.portlet.AlfrescoFacesPortlet;
import org.alfresco.web.bean.repository.User;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* @author Kevin Roast
*/
public final class AuthenticationHelper
{
public final static String AUTHENTICATION_USER = "_alfAuthTicket";
public static boolean authenticate(ServletContext context, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException
{
// examine the appropriate session for our User object
User user;
if (Application.inPortalServer() == false)
{
user = (User)httpRequest.getSession().getAttribute(AUTHENTICATION_USER);
}
else
{
user = (User)httpRequest.getSession().getAttribute(AlfrescoFacesPortlet.MANAGED_BEAN_PREFIX + AUTHENTICATION_USER);
}
if (user == null)
{
// no user/ticket - redirect to login page
httpResponse.sendRedirect(httpRequest.getContextPath() + "/faces" + Application.getLoginPage(context));
return false;
}
else
{
// setup the authentication context
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
AuthenticationService auth = (AuthenticationService)ctx.getBean("authenticationService");
auth.validate(user.getTicket());
// Set the current locale
I18NUtil.setLocale(Application.getLanguage(httpRequest.getSession()));
return true;
}
}
public static boolean authenticate(ServletContext context, HttpServletRequest httpRequest, HttpServletResponse httpResponse, String ticket)
throws IOException
{
// setup the authentication context
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
AuthenticationService auth = (AuthenticationService)ctx.getBean("authenticationService");
try
{
auth.validate(ticket);
}
catch (AuthenticationException authErr)
{
return false;
}
// Set the current locale
I18NUtil.setLocale(Application.getLanguage(httpRequest.getSession()));
return true;
}
}

View File

@@ -0,0 +1,297 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.LoginBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Servlet responsible for streaming node content from the repo directly to the response stream.
* The appropriate mimetype is calculated based on filename extension.
* <p>
* The URL to the servlet should be generated thus:
* <pre>/alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
* or
* <pre>/alfresco/download/direct/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf</pre>
* <p>
* The store protocol, followed by the store ID, followed by the content Node Id
* the last element is used for mimetype calculation and browser default filename.
* <p>
* The 'attach' or 'direct' element is used to indicate whether to display the stream directly
* in the browser or download it as a file attachment.
* <p>
* By default, the download assumes that the content is on the
* {@link org.alfresco.model.ContentModel#PROP_CONTENT content property}.<br>
* To retrieve the content of a specific model property, use a 'property' arg, providing the workspace,
* node ID AND the qualified name of the property.
* <p>
* The URL may be followed by a valid ticket argument for authentication: ?ticket=1234567890
*
* @author Kevin Roast
*/
public class DownloadContentServlet extends HttpServlet
{
private static final long serialVersionUID = -4558907921887235966L;
private static Log logger = LogFactory.getLog(DownloadContentServlet.class);
private static final String DOWNLOAD_URL = "/download/attach/{0}/{1}/{2}/{3}";
private static final String BROWSER_URL = "/download/direct/{0}/{1}/{2}/{3}";
private static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
private static final String ARG_PROPERTY = "property";
private static final String ARG_ATTACH = "attach";
private static final String ARG_TICKET = "ticket";
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
ServletOutputStream out = res.getOutputStream();
try
{
// The URL contains multiple parts
// /alfresco/download/attach/workspace/SpacesStore/0000-0000-0000-0000/myfile.pdf
// the protocol, followed by the store, followed by the Id
// the last part is only used for mimetype and browser use
// may be followed by valid ticket for pre-authenticated usage: ?ticket=1234567890
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
// see if a ticket has been supplied
String ticket = req.getParameter(ARG_TICKET);
if (ticket == null || ticket.length() == 0)
{
if (AuthenticationHelper.authenticate(getServletContext(), req, res) == false)
{
// authentication failed - no point returning the content as we haven't logged in yet
// so end servlet execution and save the URL so the login page knows what to do later
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
return;
}
}
else
{
AuthenticationHelper.authenticate(getServletContext(), req, res, ticket);
}
// TODO: add compression here?
// see http://servlets.com/jservlet2/examples/ch06/ViewResourceCompress.java for example
// only really needed if we don't use the built in compression of the servlet container
StringTokenizer t = new StringTokenizer(uri, "/");
if (t.countTokens() < 7)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
t.nextToken(); // skip web app name
t.nextToken(); // skip servlet name
String attachToken = t.nextToken();
boolean attachment = attachToken.equals(ARG_ATTACH);
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
String id = t.nextToken();
String filename = t.nextToken();
// get property qualified name
QName propertyQName = null;
String property = req.getParameter(ARG_PROPERTY);
if (property == null || property.length() == 0)
{
propertyQName = ContentModel.PROP_CONTENT;
}
else
{
propertyQName = QName.createQName(property);
}
NodeRef nodeRef = new NodeRef(storeRef, id);
if (logger.isDebugEnabled())
{
logger.debug("Found NodeRef: " + nodeRef.toString());
logger.debug("Will use filename: " + filename);
logger.debug("For property: " + propertyQName);
logger.debug("With attachment mode: " + attachment);
}
if (attachment == true)
{
// set header based on filename - will force a Save As from the browse if it doesn't recognise it
// this is better than the default response of the browse trying to display the contents!
// TODO: make this configurable - and check it does not prevent streaming of large files
res.setHeader("Content-Disposition", "attachment;filename=\"" + URLDecoder.decode(filename, "UTF-8") + '"');
}
// get the services we need to retrieve the content
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
ServiceRegistry serviceRegistry = (ServiceRegistry)context.getBean(ServiceRegistry.SERVICE_REGISTRY);
ContentService contentService = serviceRegistry.getContentService();
// get the content reader
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
// ensure that it is safe to use
reader = FileContentReader.getSafeContentReader(
reader,
Application.getMessage(req.getSession(), MSG_ERROR_CONTENT_MISSING),
nodeRef, reader);
String mimetype = reader.getMimetype();
// fall back if unable to resolve mimetype property
if (mimetype == null || mimetype.length() == 0)
{
MimetypeService mimetypeMap = serviceRegistry.getMimetypeService();
mimetype = MIMETYPE_OCTET_STREAM;
int extIndex = filename.lastIndexOf('.');
if (extIndex != -1)
{
String ext = filename.substring(extIndex + 1);
String mt = mimetypeMap.getMimetypesByExtension().get(ext);
if (mt != null)
{
mimetype = mt;
}
}
}
res.setContentType(mimetype);
// get the content and stream directly to the response output stream
// assuming the repo is capable of streaming in chunks, this should allow large files
// to be streamed directly to the browser response stream.
try
{
reader.getContent( res.getOutputStream() );
}
catch (SocketException e)
{
if (e.getMessage().contains("ClientAbortException"))
{
// the client cut the connection - our mission was accomplished apart from a little error message
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n content: " + reader);
}
else
{
throw e;
}
}
}
catch (Throwable err)
{
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
}
finally
{
out.close();
}
}
/**
* Helper to generate a URL to a content node for downloading content from the server.
* The content is supplied as an HTTP1.1 attachment to the response. This generally means
* a browser should prompt the user to save the content to specified location.
*
* @param ref NodeRef of the content node to generate URL for (cannot be null)
* @param name File name to return in the URL (cannot be null)
*
* @return URL to download the content from the specified node
*/
public final static String generateDownloadURL(NodeRef ref, String name)
{
String url = null;
try
{
url = MessageFormat.format(DOWNLOAD_URL, new Object[] {
ref.getStoreRef().getProtocol(),
ref.getStoreRef().getIdentifier(),
ref.getId(),
URLEncoder.encode(name, "US-ASCII") } );
}
catch (UnsupportedEncodingException uee)
{
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
}
return url;
}
/**
* Helper to generate a URL to a content node for downloading content from the server.
* The content is supplied directly in the reponse. This generally means a browser will
* attempt to open the content directly if possible, else it will prompt to save the file.
*
* @param ref NodeRef of the content node to generate URL for (cannot be null)
* @param name File name to return in the URL (cannot be null)
*
* @return URL to download the content from the specified node
*/
public final static String generateBrowserURL(NodeRef ref, String name)
{
String url = null;
try
{
url = MessageFormat.format(BROWSER_URL, new Object[] {
ref.getStoreRef().getProtocol(),
ref.getStoreRef().getIdentifier(),
ref.getId(),
URLEncoder.encode(name, "US-ASCII") } );
}
catch (UnsupportedEncodingException uee)
{
throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + ref, uee);
}
return url;
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.web.bean.LoginBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Servlet allowing external URL access to various global JSF views in the Web Client.
* <p>
* The servlet accepts a well formed URL that can easily be generated from a Content or Space NodeRef.
* The URL also specifies the JSF "outcome" to be executed which provides the correct JSF View to be
* displayed. The JSF "outcome" must equate to a global navigation rule or it will not be displayed.
* Servlet URL is of the form:
* <p>
* <code>http://&lt;server&gt;/alfresco/navigate/&lt;outcome&gt;[/&lt;workspace&gt;/&lt;store&gt;/&lt;nodeId&gt;]</code> or <br/>
* <code>http://&lt;server&gt;/alfresco/navigate/&lt;outcome&gt;[/webdav/&lt;path/to/node&gt;]</code>
* </p>
*
* @author Kevin Roast
*/
public class ExternalAccessServlet extends HttpServlet
{
private static final long serialVersionUID = -4118907921337237802L;
private static Log logger = LogFactory.getLog(ExternalAccessServlet.class);
/**
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
boolean alreadyAuthenticated = AuthenticationHelper.authenticate(getServletContext(), req, res);
// The URL contains multiple parts
// /alfresco/navigate/<outcome>
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri);
StringTokenizer t = new StringTokenizer(uri, "/");
int count = t.countTokens();
if (count < 3)
{
throw new IllegalArgumentException("Externally addressable URL did not contain all required args: " + uri);
}
t.nextToken(); // skip web app name
t.nextToken(); // skip servlet name
String outcome = t.nextToken();
// get rest of the tokens
String[] tokens = new String[count - 3];
for (int i=0; i<count - 3; i++)
{
tokens[i] = t.nextToken();
}
// set the session variable so the login bean knows which outcome to use
req.getSession().setAttribute(LoginBean.LOGIN_OUTCOME_KEY, outcome);
// set the args if any
req.getSession().setAttribute(LoginBean.LOGIN_OUTCOME_ARGS, tokens);
if (alreadyAuthenticated)
{
// clear the User object from the Session - this will force a relogin
// we do this so the outcome from the login page can then be changed
req.getSession().removeAttribute(AuthenticationHelper.AUTHENTICATION_USER);
if (logger.isDebugEnabled())
logger.debug("Removing User session - will redirect via login page...");
// redirect to root URL will force the login page to appear via the Authentication Filter
res.sendRedirect(req.getContextPath());
}
}
/**
* Generate a URL to the External Access Servlet.
* Allows access to JSF views (via an "outcome" ID) from external URLs.
*
* @param outcome
* @param args
*
* @return URL
*/
public final static String generateExternalURL(String outcome, String args)
{
if (args == null)
{
return MessageFormat.format(EXTERNAL_URL, new Object[] {outcome} );
}
else
{
return MessageFormat.format(EXTERNAL_URL_ARGS, new Object[] {outcome, args} );
}
}
// example: http://<server>/alfresco/navigate/<outcome>[/<workspace>/<store>/<nodeId>]
private static final String EXTERNAL_URL = "/navigate/{0}";
private static final String EXTERNAL_URL_ARGS = "/navigate/{0}/{1}";
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.alfresco.web.app.Application;
/**
* Filter that determines whether the application is running inside a portal
* server or servlet engine. The fact that this filter gets called means
* the application is running inside a servlet engine.
*
* @author gavinc
*/
public class ModeDetectionFilter implements Filter
{
/**
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
public void init(FilterConfig config) throws ServletException
{
// nothing to do
}
/**
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException
{
// as we get here means we are inside a servlet engine as portal servers
// do not support the calling of filters yet
Application.setInPortalServer(false);
// continue filter chaining
chain.doFilter(req, res);
}
/**
* @see javax.servlet.Filter#destroy()
*/
public void destroy()
{
// nothing to do
}
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.IOException;
import java.net.SocketException;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.transaction.UserTransaction;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.template.DateCompareMethod;
import org.alfresco.repo.template.HasAspectMethod;
import org.alfresco.repo.template.I18NMessageMethod;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateException;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Servlet responsible for streaming content from a template processed against a node directly
* to the response stream.
* <p>
* The URL to the servlet should be generated thus:
* <pre>/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000
* or
* <pre>/alfresco/template/workspace/SpacesStore/0000-0000-0000-0000/workspace/SpacesStore/0000-0000-0000-0000
* <p>
* The store protocol, followed by the store ID, followed by the content Node Id used to
* identify the node to execute the default template for. The second set of elements encode
* the store and node Id of the template to used if a default is not set or not requested.
* <p>
* The URL may be followed by a valid 'ticket' argument for authentication: ?ticket=1234567890
* <br>
* And may be followed by a 'mimetype' argument specifying the mimetype to return the result as
* on the stream. Otherwise it is assumed that HTML is the default response mimetype.
*
* @author Kevin Roast
*/
public class TemplateContentServlet extends HttpServlet
{
private static final String MIMETYPE_HTML = "text/html";
private static final long serialVersionUID = -4123407921997235977L;
private static Log logger = LogFactory.getLog(TemplateContentServlet.class);
private static final String DEFAULT_URL = "/template/{0}/{1}/{2}";
private static final String TEMPALTE_URL = "/template/{0}/{1}/{2}/{3}/{4}/{5}";
private static final String MSG_ERROR_CONTENT_MISSING = "error_content_missing";
private static final String ARG_TICKET = "ticket";
private static final String ARG_MIMETYPE = "mimetype";
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
try
{
String uri = req.getRequestURI();
if (logger.isDebugEnabled())
logger.debug("Processing URL: " + uri + (req.getQueryString() != null ? ("?" + req.getQueryString()) : ""));
// see if a ticket has been supplied
String ticket = req.getParameter(ARG_TICKET);
if (ticket == null || ticket.length() == 0)
{
if (AuthenticationHelper.authenticate(getServletContext(), req, res) == false)
{
// authentication failed - no point returning the content as we haven't logged in yet
// so end servlet execution and save the URL so the login page knows what to do later
req.getSession().setAttribute(LoginBean.LOGIN_REDIRECT_KEY, uri);
return;
}
}
else
{
AuthenticationHelper.authenticate(getServletContext(), req, res, ticket);
}
StringTokenizer t = new StringTokenizer(uri, "/");
int tokenCount = t.countTokens();
if (tokenCount < 5)
{
throw new IllegalArgumentException("Download URL did not contain all required args: " + uri);
}
t.nextToken(); // skip web app name
t.nextToken(); // skip servlet name
// get NodeRef to the content
StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken());
NodeRef nodeRef = new NodeRef(storeRef, t.nextToken());
// get NodeRef to the template if supplied
NodeRef templateRef = null;
if (tokenCount == 8)
{
storeRef = new StoreRef(t.nextToken(), t.nextToken());
templateRef = new NodeRef(storeRef, t.nextToken());
}
// get the services we need to retrieve the content
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
ServiceRegistry serviceRegistry = (ServiceRegistry)context.getBean(ServiceRegistry.SERVICE_REGISTRY);
NodeService nodeService = serviceRegistry.getNodeService();
TemplateService templateService = serviceRegistry.getTemplateService();
UserTransaction txn = null;
try
{
txn = serviceRegistry.getTransactionService().getUserTransaction(true);
txn.begin();
// if template not supplied, then use the default against the node
if (templateRef == null)
{
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPLATABLE))
{
templateRef = (NodeRef)nodeService.getProperty(nodeRef, ContentModel.PROP_TEMPLATE);
}
if (templateRef == null)
{
throw new TemplateException("Template reference not set against node or not supplied in URL.");
}
}
// create the model - put the supplied noderef in as space/document as appropriate
Object model = getModel(serviceRegistry, req.getSession(), nodeRef);
// process the template against the node content directly to the response output stream
// assuming the repo is capable of streaming in chunks, this should allow large files
// to be streamed directly to the browser response stream.
try
{
templateService.processTemplate(
null,
templateRef.toString(),
model,
res.getWriter());
// commit the transaction
txn.commit();
}
catch (SocketException e)
{
if (e.getMessage().contains("ClientAbortException"))
{
// the client cut the connection - our mission was accomplished apart from a little error message
logger.error("Client aborted stream read:\n node: " + nodeRef + "\n template: " + templateRef);
try { if (txn != null) {txn.rollback();} } catch (Exception tex) {}
}
else
{
throw e;
}
}
}
catch (Throwable txnErr)
{
try { if (txn != null) {txn.rollback();} } catch (Exception tex) {}
}
}
catch (Throwable err)
{
throw new AlfrescoRuntimeException("Error during download content servlet processing: " + err.getMessage(), err);
}
}
private Object getModel(ServiceRegistry services, HttpSession session, NodeRef nodeRef)
{
// create FreeMarker default model and merge
Map root = new HashMap(11, 1.0f);
// supply the CompanyHome space as "companyhome"
NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
TemplateNode companyRootNode = new TemplateNode(companyRootRef, services, imageResolver);
root.put("companyhome", companyRootNode);
// supply the users Home Space as "userhome"
User user = Application.getCurrentUser(session);
NodeRef userRootRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId());
TemplateNode userRootNode = new TemplateNode(userRootRef, services, imageResolver);
root.put("userhome", userRootNode);
// put the current NodeRef in as "space" and "document"
TemplateNode node = new TemplateNode(nodeRef, services, imageResolver);
root.put("space", node);
root.put("document", node);
// supply the current user Node as "person"
root.put("person", new TemplateNode(user.getPerson(), services, imageResolver));
// current date/time is useful to have and isn't supplied by FreeMarker by default
root.put("date", new Date());
// add custom method objects
root.put("hasAspect", new HasAspectMethod());
root.put("message", new I18NMessageMethod());
root.put("dateCompare", new DateCompareMethod());
return root;
}
/** Template Image resolver helper */
private TemplateImageResolver imageResolver = new TemplateImageResolver()
{
public String resolveImagePathForName(String filename, boolean small)
{
return Utils.getFileTypeImage(filename, small);
}
};
/**
* Helper to generate a URL to a content node for downloading content from the server.
* The content is supplied as an HTTP1.1 attachment to the response. This generally means
* a browser should prompt the user to save the content to specified location.
*
* @param ref NodeRef of the content node to generate URL for (cannot be null)
* @param name File name to return in the URL (cannot be null)
*
* @return URL to download the content from the specified node
*/
public final static String generateURL(NodeRef ref, String name)
{
return MessageFormat.format(DEFAULT_URL, new Object[] {
ref.getStoreRef().getProtocol(),
ref.getStoreRef().getIdentifier(),
ref.getId() } );
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.app.servlet;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.util.TempFileProvider;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.FileUploadBean;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Servlet that takes a file uploaded via a browser and represents it as an
* UploadFileBean in the session
*
* @author gavinc
*/
public class UploadFileServlet extends HttpServlet
{
private static final long serialVersionUID = -5482538466491052873L;
private static Log logger = LogFactory.getLog(UploadFileServlet.class);
/**
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String returnPage = null;
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
try
{
AuthenticationHelper.authenticate(getServletContext(), request, response);
if (isMultipart == false)
{
throw new AlfrescoRuntimeException("This servlet can only be used to handle file upload requests, make" +
"sure you have set the enctype attribute on your form to multipart/form-data");
}
if (logger.isDebugEnabled())
logger.debug("Uploading servlet servicing...");
HttpSession session = request.getSession();
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
List<FileItem> fileItems = upload.parseRequest(request);
Iterator<FileItem> iter = fileItems.iterator();
FileUploadBean bean = new FileUploadBean();
while(iter.hasNext())
{
FileItem item = iter.next();
if(item.isFormField())
{
if (item.getFieldName().equalsIgnoreCase("return-page"))
{
returnPage = item.getString();
}
}
else
{
String filename = item.getName();
if (filename != null && filename.length() != 0)
{
if (logger.isDebugEnabled())
logger.debug("Processing uploaded file: " + filename);
// workaround a bug in IE where the full path is returned
// IE is only available for Windows so only check for the Windows path separator
int idx = filename.lastIndexOf('\\');
if (idx == -1)
{
// if there is no windows path separator check for *nix
idx = filename.lastIndexOf('/');
}
if (idx != -1)
{
filename = filename.substring(idx + File.separator.length());
}
File tempFile = TempFileProvider.createTempFile("alfresco", ".upload");
item.write(tempFile);
bean.setFile(tempFile);
bean.setFileName(filename);
bean.setFilePath(tempFile.getAbsolutePath());
session.setAttribute(FileUploadBean.FILE_UPLOAD_BEAN_NAME, bean);
if (logger.isDebugEnabled())
logger.debug("Temp file: " + tempFile.getAbsolutePath() + " created from upload filename: " + filename);
}
}
}
if (returnPage == null || returnPage.length() == 0)
{
throw new AlfrescoRuntimeException("return-page parameter has not been supplied");
}
// finally redirect
if (logger.isDebugEnabled())
logger.debug("Upload servicing complete, redirecting to: " + returnPage);
response.sendRedirect(returnPage);
}
catch (Throwable error)
{
Application.handleServletError(getServletContext(), (HttpServletRequest)request,
(HttpServletResponse)response, error, logger, returnPage);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.alfresco.config.xml.XMLConfigService;
import org.alfresco.web.app.Application;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Bean used for administration purposes
*
* @author gavinc
*/
public class AdminBean
{
/**
* Resets the config service
*
* @param event Event that caused the request for the reset
*/
public void resetConfigService(ActionEvent event)
{
// get the config service
XMLConfigService configSvc = (XMLConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
// reset it
configSvc.reset();
}
}

View File

@@ -0,0 +1,741 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.search.ISO9075;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.web.app.servlet.DownloadContentServlet;
// TODO: DownloadServlet - use of request parameter for property name?
// TODO: Anyway to switch content view url link / property value text?
/**
* Backing bean to support the Admin Node Browser
*/
public class AdminNodeBrowseBean
{
/** selected query language */
private String queryLanguage = null;
/** available query languages */
private static List<SelectItem> queryLanguages = new ArrayList<SelectItem>();
static
{
queryLanguages.add(new SelectItem("noderef"));
queryLanguages.add(new SelectItem(SearchService.LANGUAGE_XPATH));
queryLanguages.add(new SelectItem(SearchService.LANGUAGE_LUCENE));
}
// query and results
private String query = null;
private SearchResults searchResults = new SearchResults(null);
// stores and node
private DataModel stores = null;
private NodeRef nodeRef = null;
private QName nodeType = null;
private Path primaryPath = null;
private DataModel parents = null;
private DataModel aspects = null;
private DataModel properties = null;
private DataModel children = null;
private DataModel assocs = null;
// supporting repository services
private NodeService nodeService;
private DictionaryService dictionaryService;
private SearchService searchService;
/**
* @param nodeService node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param searchService search service
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param dictionaryService dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* Gets the list of repository stores
*
* @return stores
*/
public DataModel getStores()
{
if (stores == null)
{
List<StoreRef> storeRefs = nodeService.getStores();
stores = new ListDataModel(storeRefs);
}
return stores;
}
/**
* Gets the selected node reference
*
* @return node reference (defaults to system store root)
*/
public NodeRef getNodeRef()
{
if (nodeRef == null)
{
nodeRef = nodeService.getRootNode(new StoreRef("system", "system"));
}
return nodeRef;
}
/**
* Sets the selected node reference
*
* @param nodeRef node reference
*/
private void setNodeRef(NodeRef nodeRef)
{
this.nodeRef = nodeRef;
// clear cache
primaryPath = null;
nodeType = null;
parents = null;
aspects = null;
properties = null;
children = null;
assocs = null;
}
/**
* Gets the current node type
*
* @return node type
*/
public QName getNodeType()
{
if (nodeType == null)
{
nodeType = nodeService.getType(getNodeRef());
}
return nodeType;
}
/**
* Gets the current node primary path
*
* @return primary path
*/
public String getPrimaryPath()
{
if (primaryPath == null)
{
primaryPath = nodeService.getPath(getNodeRef());
}
return ISO9075.decode(primaryPath.toString());
}
/**
* Gets the current node primary parent reference
*
* @return primary parent ref
*/
public NodeRef getPrimaryParent()
{
getPrimaryPath();
Path.Element element = primaryPath.last();
NodeRef parentRef = ((Path.ChildAssocElement)element).getRef().getParentRef();
return parentRef;
}
/**
* Gets the current node aspects
*
* @return node aspects
*/
public DataModel getAspects()
{
if (aspects == null)
{
List<QName> aspectNames = new ArrayList<QName>(nodeService.getAspects(getNodeRef()));
aspects = new ListDataModel(aspectNames);
}
return aspects;
}
/**
* Gets the current node parents
*
* @return node parents
*/
public DataModel getParents()
{
if (parents == null)
{
List<ChildAssociationRef> parentRefs = nodeService.getParentAssocs(getNodeRef());
parents = new ListDataModel(parentRefs);
}
return parents;
}
/**
* Gets the current node properties
*
* @return properties
*/
public DataModel getProperties()
{
if (properties == null)
{
Map<QName, Serializable> propertyValues = nodeService.getProperties(getNodeRef());
List<Property> nodeProperties = new ArrayList<Property>(propertyValues.size());
for (Map.Entry<QName, Serializable> property : propertyValues.entrySet())
{
nodeProperties.add(new Property(property.getKey(), property.getValue()));
}
properties = new ListDataModel(nodeProperties);
}
return properties;
}
/**
* Gets the current node children
*
* @return node children
*/
public DataModel getChildren()
{
if (children == null)
{
List<ChildAssociationRef> assocRefs = nodeService.getChildAssocs(getNodeRef());
children = new ListDataModel(assocRefs);
}
return children;
}
/**
* Gets the current node associations
*
* @return associations
*/
public DataModel getAssocs()
{
if (assocs == null)
{
List<AssociationRef> assocRefs = nodeService.getTargetAssocs(getNodeRef(), RegexQNamePattern.MATCH_ALL);
assocs = new ListDataModel(assocRefs);
}
return assocs;
}
/**
* Gets the current query language
*
* @return query language
*/
public String getQueryLanguage()
{
return queryLanguage;
}
/**
* Sets the current query language
*
* @param queryLanguage query language
*/
public void setQueryLanguage(String queryLanguage)
{
this.queryLanguage = queryLanguage;
}
/**
* Gets the current query
*
* @return query statement
*/
public String getQuery()
{
return query;
}
/**
* Set the current query
*
* @param query query statement
*/
public void setQuery(String query)
{
this.query = query;
}
/**
* Gets the list of available query languages
*
* @return query languages
*/
public List getQueryLanguages()
{
return queryLanguages;
}
/**
* Gets the current search results
*
* @return search results
*/
public SearchResults getSearchResults()
{
return searchResults;
}
/**
* Action to select a store
*
* @return next action
*/
public String selectStore()
{
StoreRef storeRef = (StoreRef)stores.getRowData();
NodeRef rootNode = nodeService.getRootNode(storeRef);
setNodeRef(rootNode);
return "success";
}
/**
* Action to select stores list
*
* @return next action
*/
public String selectStores()
{
stores = null;
return "success";
}
/**
* Action to select primary path
*
* @return next action
*/
public String selectPrimaryPath()
{
// force refresh of self
setNodeRef(nodeRef);
return "success";
}
/**
* Action to select primary parent
*
* @return next action
*/
public String selectPrimaryParent()
{
setNodeRef(getPrimaryParent());
return "success";
}
/**
* Action to select parent
*
* @return next action
*/
public String selectParent()
{
ChildAssociationRef assocRef = (ChildAssociationRef)parents.getRowData();
NodeRef parentRef = assocRef.getParentRef();
setNodeRef(parentRef);
return "success";
}
/**
* Action to select association To node
*
* @return next action
*/
public String selectToNode()
{
AssociationRef assocRef = (AssociationRef)assocs.getRowData();
NodeRef targetRef = assocRef.getTargetRef();
setNodeRef(targetRef);
return "success";
}
/**
* Action to select node property
*
* @return next action
*/
public String selectNodeProperty()
{
Property property = (Property)properties.getRowData();
Property.Value value = (Property.Value)property.getValues().getRowData();
NodeRef nodeRef = (NodeRef)value.getValue();
setNodeRef(nodeRef);
return "success";
}
/**
* Action to select child
*
* @return next action
*/
public String selectChild()
{
ChildAssociationRef assocRef = (ChildAssociationRef)children.getRowData();
NodeRef childRef = assocRef.getChildRef();
setNodeRef(childRef);
return "success";
}
/**
* Action to select search result node
*
* @return next action
*/
public String selectResultNode()
{
ChildAssociationRef assocRef = (ChildAssociationRef)searchResults.getRows().getRowData();
NodeRef childRef = assocRef.getChildRef();
setNodeRef(childRef);
return "success";
}
/**
* Action to submit search
*
* @return next action
*/
public String submitSearch()
{
try
{
if (queryLanguage.equals("noderef"))
{
// ensure node exists
NodeRef nodeRef = new NodeRef(query);
boolean exists = nodeService.exists(nodeRef);
if (!exists)
{
throw new AlfrescoRuntimeException("Node " + nodeRef + " does not exist.");
}
setNodeRef(nodeRef);
return "node";
}
// perform search
searchResults = new SearchResults(searchService.query(getNodeRef().getStoreRef(), queryLanguage, query));
return "search";
}
catch(Throwable e)
{
FacesContext context = FacesContext.getCurrentInstance();
FacesMessage message = new FacesMessage();
message.setSeverity(FacesMessage.SEVERITY_ERROR);
message.setDetail("Search failed due to: " + e.toString());
context.addMessage("searchForm:query", message);
return "error";
}
}
/**
* Property wrapper class
*/
public class Property
{
private QName name;
private boolean isCollection = false;
private DataModel values;
private String datatype;
private String residual;
/**
* Construct
*
* @param name property name
* @param value property values
*/
public Property(QName name, Serializable value)
{
this.name = name;
PropertyDefinition propDef = dictionaryService.getProperty(name);
if (propDef != null)
{
datatype = propDef.getDataType().getName().toString();
residual = "false";
}
else
{
residual = "true";
}
// handle multi/single values
// TODO: perhaps this is not the most efficient way - lots of list creations
List<Value> values = new ArrayList<Value>();
if (value instanceof Collection)
{
isCollection = true;
for (Serializable multiValue : (Collection<Serializable>)value)
{
values.add(new Value(multiValue));
}
}
else
{
values.add(new Value(value));
}
this.values = new ListDataModel(values);
}
/**
* Gets the property name
*
* @return name
*/
public QName getName()
{
return name;
}
/**
* Gets the property data type
*
* @return data type
*/
public String getDataType()
{
return datatype;
}
/**
* Gets the property value
*
* @return value
*/
public DataModel getValues()
{
return values;
}
/**
* Determines whether the property is residual
*
* @return true => property is not defined in dictionary
*/
public String getResidual()
{
return residual;
}
/**
* Determines whether the property is of ANY type
*
* @return true => is any
*/
public boolean isAny()
{
return (datatype == null) ? false : datatype.equals(DataTypeDefinition.ANY.toString());
}
/**
* Determines whether the property is a collection
*
* @return true => is collection
*/
public boolean isCollection()
{
return isCollection;
}
/**
* Value wrapper
*/
public class Value
{
private Serializable value;
/**
* Construct
*
* @param value value
*/
public Value(Serializable value)
{
this.value = value;
}
/**
* Gets the value
*
* @return the value
*/
public Serializable getValue()
{
return value;
}
/**
* Gets the value datatype
*
* @return the value datatype
*/
public String getDataType()
{
String datatype = Property.this.getDataType();
if (datatype == null || datatype.equals(DataTypeDefinition.ANY.toString()))
{
if (value != null)
{
datatype = dictionaryService.getDataType(value.getClass()).getName().toString();
}
}
return datatype;
}
/**
* Gets the download url (for content properties)
*
* @return url
*/
public String getUrl()
{
String url = FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath();
url += DownloadContentServlet.generateBrowserURL(nodeRef, "file.bin");
url += "?property=" + name;
return url;
}
/**
* Determines whether the value is content
*
* @return true => is content
*/
public boolean isContent()
{
String datatype = getDataType();
return (datatype == null) ? false : datatype.equals(DataTypeDefinition.CONTENT.toString());
}
/**
* Determines whether the value is a node ref
*
* @return true => is node ref
*/
public boolean isNodeRef()
{
String datatype = getDataType();
return (datatype == null) ? false : datatype.equals(DataTypeDefinition.NODE_REF.toString()) || datatype.equals(DataTypeDefinition.CATEGORY.toString());
}
/**
* Determines whether the value is null
*
* @return true => value is null
*/
public boolean isNullValue()
{
return value == null;
}
}
}
/**
* Wrapper class for Search Results
*/
public class SearchResults
{
private int length = 0;
private DataModel rows;
/**
* Construct
*
* @param resultSet query result set
*/
public SearchResults(ResultSet resultSet)
{
rows = new ListDataModel();
if (resultSet != null)
{
rows.setWrappedData(resultSet.getChildAssocRefs());
length = resultSet.length();
}
}
/**
* Gets the row count
*
* @return count of rows
*/
public int getLength()
{
return length;
}
/**
* Gets the rows
*
* @return the rows
*/
public DataModel getRows()
{
return rows;
}
}
}

View File

@@ -0,0 +1,829 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.CachingDateFormat;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.config.ClientConfigElement;
import org.alfresco.web.config.ClientConfigElement.CustomProperty;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.alfresco.web.ui.common.component.UIPanel.ExpandedEvent;
import org.alfresco.web.ui.repo.component.UISearchCustomProperties;
/**
* Provides the form state and action event handling for the Advanced Search UI.
* <p>
* Integrates with the web-client ConfigService to retrieve configuration of custom
* meta-data searching fields. Custom fields can be configured to appear in the UI
* and they are they automatically added to the search query by this bean.
*
* @author Kevin Roast
*/
public class AdvancedSearchBean
{
/**
* Default constructor
*/
public AdvancedSearchBean()
{
// initial state of progressive panels that don't use the default
panels.put("categories-panel", false);
panels.put("attrs-panel", false);
panels.put("custom-panel", false);
}
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param namespaceService The NamespaceService to set.
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @return Returns the progressive panels expanded state map.
*/
public Map<String, Boolean> getPanels()
{
return this.panels;
}
/**
* @param panels The progressive panels expanded state map.
*/
public void setPanels(Map<String, Boolean> panels)
{
this.panels = panels;
}
/**
* @return Returns the folder to search, null for all.
*/
public String getLookin()
{
return this.lookin;
}
/**
* @param lookin The folder to search in or null for all.
*/
public void setLookin(String lookIn)
{
this.lookin = lookIn;
}
/**
* @return Returns the location.
*/
public NodeRef getLocation()
{
return this.location;
}
/**
* @param location The location to set.
*/
public void setLocation(NodeRef location)
{
this.location = location;
}
/**
* @return Returns the search mode.
*/
public String getMode()
{
return this.mode;
}
/**
* @param mode The search mode to set.
*/
public void setMode(String mode)
{
this.mode = mode;
}
/**
* @return Returns the text to search for.
*/
public String getText()
{
return this.text;
}
/**
* @param text The text to set.
*/
public void setText(String text)
{
this.text = text;
}
/**
* @return Returns the category.
*/
public NodeRef getCategory()
{
return this.category;
}
/**
* @param category The category to set.
*/
public void setCategory(NodeRef category)
{
this.category = category;
}
/**
* @return Returns true to search location children, false for just the specified location.
*/
public boolean getLocationChildren()
{
return this.locationChildren;
}
/**
* @param locationChildren True to search location children, false for just the specified location.
*/
public void setLocationChildren(boolean locationChildren)
{
this.locationChildren = locationChildren;
}
/**
* @return Returns true to search category children, false for just the specified category.
*/
public boolean getCategoryChildren()
{
return this.categoryChildren;
}
/**
* @param categoryChildren True to search category children, false for just the specified category.
*/
public void setCategoryChildren(boolean categoryChildren)
{
this.categoryChildren = categoryChildren;
}
/**
* @return Returns the createdDateFrom.
*/
public Date getCreatedDateFrom()
{
return this.createdDateFrom;
}
/**
* @param createdDateFrom The createdDateFrom to set.
*/
public void setCreatedDateFrom(Date createdDate)
{
this.createdDateFrom = createdDate;
}
/**
* @return Returns the description.
*/
public String getDescription()
{
return this.description;
}
/**
* @param description The description to set.
*/
public void setDescription(String description)
{
this.description = description;
}
/**
* @return Returns the modifiedDateFrom.
*/
public Date getModifiedDateFrom()
{
return this.modifiedDateFrom;
}
/**
* @param modifiedDateFrom The modifiedDateFrom to set.
*/
public void setModifiedDateFrom(Date modifiedDate)
{
this.modifiedDateFrom = modifiedDate;
}
/**
* @return Returns the createdDateTo.
*/
public Date getCreatedDateTo()
{
return this.createdDateTo;
}
/**
* @param createdDateTo The createdDateTo to set.
*/
public void setCreatedDateTo(Date createdDateTo)
{
this.createdDateTo = createdDateTo;
}
/**
* @return Returns the modifiedDateTo.
*/
public Date getModifiedDateTo()
{
return this.modifiedDateTo;
}
/**
* @param modifiedDateTo The modifiedDateTo to set.
*/
public void setModifiedDateTo(Date modifiedDateTo)
{
this.modifiedDateTo = modifiedDateTo;
}
/**
* @return Returns the title.
*/
public String getTitle()
{
return this.title;
}
/**
* @param title The title to set.
*/
public void setTitle(String title)
{
this.title = title;
}
/**
* @return Returns the author.
*/
public String getAuthor()
{
return this.author;
}
/**
* @param author The author to set.
*/
public void setAuthor(String author)
{
this.author = author;
}
/**
* @return Returns the modifiedDateChecked.
*/
public boolean isModifiedDateChecked()
{
return this.modifiedDateChecked;
}
/**
* @param modifiedDateChecked The modifiedDateChecked to set.
*/
public void setModifiedDateChecked(boolean modifiedDateChecked)
{
this.modifiedDateChecked = modifiedDateChecked;
}
/**
* @return Returns the createdDateChecked.
*/
public boolean isCreatedDateChecked()
{
return this.createdDateChecked;
}
/**
* @return Returns the content type currenty selected
*/
public String getContentType()
{
return this.contentType;
}
/**
* @param contentType Sets the currently selected content type
*/
public void setContentType(String contentType)
{
this.contentType = contentType;
}
/**
* @return Returns the contentFormat.
*/
public String getContentFormat()
{
return this.contentFormat;
}
/**
* @param contentFormat The contentFormat to set.
*/
public void setContentFormat(String contentFormat)
{
this.contentFormat = contentFormat;
}
/**
* @return Returns the custom properties Map.
*/
public Map<String, Object> getCustomProperties()
{
return this.customProperties;
}
/**
* @param customProperties The custom properties Map to set.
*/
public void setCustomProperties(Map<String, Object> customProperties)
{
this.customProperties = customProperties;
}
/**
* @param createdDateChecked The createdDateChecked to set.
*/
public void setCreatedDateChecked(boolean createdDateChecked)
{
this.createdDateChecked = createdDateChecked;
}
/**
* @return Returns a list of content object types to allow the user to select from
*/
public List<SelectItem> getContentTypes()
{
if (this.contentTypes == null)
{
FacesContext context = FacesContext.getCurrentInstance();
// add the well known cm:content object type by default
this.contentTypes = new ArrayList<SelectItem>(5);
this.contentTypes.add(new SelectItem(ContentModel.TYPE_CONTENT.toString(),
Application.getMessage(context, MSG_CONTENT)));
// add any configured content sub-types to the list
List<String> types = getClientConfig().getContentTypes();
if (types != null)
{
DictionaryService dictionaryService = Repository.getServiceRegistry(context).getDictionaryService();
for (String type : types)
{
QName idQName = Repository.resolveToQName(type);
TypeDefinition typeDef = dictionaryService.getType(idQName);
if (typeDef != null && dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_CONTENT))
{
// try and get label from the dictionary
String label = typeDef.getTitle();
// else just use the localname
if (label == null)
{
label = idQName.getLocalName();
}
this.contentTypes.add(new SelectItem(idQName.toString(), label));
}
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.contentTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
}
return this.contentTypes;
}
/**
* @return Returns a list of content formats to allow the user to select from
*/
public List<SelectItem> getContentFormats()
{
if (this.contentFormats == null)
{
this.contentFormats = new ArrayList<SelectItem>(80);
ServiceRegistry registry = Repository.getServiceRegistry(FacesContext.getCurrentInstance());
MimetypeService mimetypeService = registry.getMimetypeService();
// get the mime type display names
Map<String, String> mimeTypes = mimetypeService.getDisplaysByMimetype();
for (String mimeType : mimeTypes.keySet())
{
this.contentFormats.add(new SelectItem(mimeType, mimeTypes.get(mimeType)));
}
// make sure the list is sorted by the values
QuickSort sorter = new QuickSort(this.contentFormats, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
// add the "All Formats" constant marker at the top of the list (default selection)
this.contentFormats.add(0, new SelectItem("", Application.getMessage(FacesContext.getCurrentInstance(), MSG_ALL_FORMATS)));
}
return this.contentFormats;
}
// ------------------------------------------------------------------------------
// Action event handlers
/**
* Handler to clear the advanced search screen form details
*/
public void reset(ActionEvent event)
{
this.text = "";
this.mode = MODE_ALL;
this.lookin = LOOKIN_ALL;
this.contentType = null;
this.location = null;
this.category = null;
this.title = null;
this.description = null;
this.author = null;
this.createdDateFrom = null;
this.modifiedDateFrom = null;
this.createdDateChecked = false;
this.modifiedDateChecked = false;
this.customProperties.clear();
}
/**
* Handler to perform a search based on the current criteria
*/
public String search()
{
String outcome = null;
if (this.text != null && this.text.length() != 0)
{
// construct the Search Context and set on the navigation bean
// then simply navigating to the browse screen will cause it pickup the Search Context
SearchContext search = new SearchContext();
search.setText(this.text);
if (this.mode.equals(MODE_ALL))
{
search.setMode(SearchContext.SEARCH_ALL);
}
else if (this.mode.equals(MODE_FILES_TEXT))
{
search.setMode(SearchContext.SEARCH_FILE_NAMES_CONTENTS);
}
else if (this.mode.equals(MODE_FILES))
{
search.setMode(SearchContext.SEARCH_FILE_NAMES);
}
else if (this.mode.equals(MODE_FOLDERS))
{
search.setMode(SearchContext.SEARCH_SPACE_NAMES);
}
// additional attributes search
if (this.description != null && this.description.length() != 0)
{
search.addAttributeQuery(ContentModel.PROP_DESCRIPTION, this.description);
}
if (this.title != null && this.title.length() != 0)
{
search.addAttributeQuery(ContentModel.PROP_TITLE, this.title);
}
if (this.author != null && this.author.length() != 0)
{
search.addAttributeQuery(ContentModel.PROP_CREATOR, this.author);
}
if (this.contentFormat != null && this.contentFormat.length() != 0)
{
search.setMimeType(this.contentFormat);
}
if (this.createdDateChecked == true)
{
SimpleDateFormat df = CachingDateFormat.getDateFormat();
String strCreatedDate = df.format(this.createdDateFrom);
String strCreatedDateTo = df.format(this.createdDateTo);
search.addRangeQuery(ContentModel.PROP_CREATED, strCreatedDate, strCreatedDateTo, true);
}
if (this.modifiedDateChecked == true)
{
SimpleDateFormat df = CachingDateFormat.getDateFormat();
String strModifiedDate = df.format(this.modifiedDateFrom);
String strModifiedDateTo = df.format(this.modifiedDateTo);
search.addRangeQuery(ContentModel.PROP_MODIFIED, strModifiedDate, strModifiedDateTo, true);
}
// walk each of the custom properties add add them as additional attributes
for (String qname : this.customProperties.keySet())
{
Object value = this.customProperties.get(qname);
DataTypeDefinition typeDef = getCustomPropertyLookup().get(qname);
if (typeDef != null)
{
QName typeName = typeDef.getName();
if (DataTypeDefinition.DATE.equals(typeName) || DataTypeDefinition.DATETIME.equals(typeName))
{
// only apply date to search if the user has checked the enable checkbox
if (value != null && Boolean.valueOf(value.toString()) == true)
{
SimpleDateFormat df = CachingDateFormat.getDateFormat();
String strDateFrom = df.format(this.customProperties.get(
UISearchCustomProperties.PREFIX_DATE_FROM + qname));
String strDateTo = df.format(this.customProperties.get(
UISearchCustomProperties.PREFIX_DATE_TO + qname));
search.addRangeQuery(QName.createQName(qname), strDateFrom, strDateTo, true);
}
}
else if (DataTypeDefinition.BOOLEAN.equals(typeName))
{
if (((Boolean)value) == true)
{
search.addFixedValueQuery(QName.createQName(qname), value.toString());
}
}
else if (DataTypeDefinition.NODE_REF.equals(typeName) || DataTypeDefinition.CATEGORY.equals(typeName))
{
if (value != null)
{
search.addFixedValueQuery(QName.createQName(qname), value.toString());
}
}
else if (DataTypeDefinition.INT.equals(typeName) || DataTypeDefinition.LONG.equals(typeName) ||
DataTypeDefinition.FLOAT.equals(typeName) || DataTypeDefinition.DOUBLE.equals(typeName))
{
String strVal = value.toString();
if (strVal != null && strVal.length() != 0)
{
search.addFixedValueQuery(QName.createQName(qname), strVal);
}
}
else
{
// by default use toString() value - this is for text fields and unknown types
String strVal = value.toString();
if (strVal != null && strVal.length() != 0)
{
search.addAttributeQuery(QName.createQName(qname), strVal);
}
}
}
}
// location path search
if (this.lookin.equals(LOOKIN_OTHER) && this.location != null)
{
search.setLocation(SearchContext.getPathFromSpaceRef(
new NodeRef(Repository.getStoreRef(), this.location.getId()), this.locationChildren));
}
// category path search
if (this.category != null)
{
search.setCategories(new String[]{SearchContext.getPathFromSpaceRef(
new NodeRef(Repository.getStoreRef(), this.category.getId()), this.categoryChildren)});
}
// content type restriction
if (this.contentType != null)
{
search.setContentType(this.contentType);
}
// set the Search Context onto the top-level navigator bean
// this causes the browse screen to switch into search results view
this.navigator.setSearchContext(search);
outcome = "browse";
}
return outcome;
}
/**
* @return ClientConfigElement
*/
private ClientConfigElement getClientConfig()
{
if (clientConfigElement == null)
{
ConfigService configService = Application.getConfigService(FacesContext.getCurrentInstance());
clientConfigElement = (ClientConfigElement)configService.getGlobalConfig().getConfigElement(
ClientConfigElement.CONFIG_ELEMENT_ID);
}
return clientConfigElement;
}
/**
* Helper map to lookup custom property QName strings against a DataTypeDefinition
*
* @return custom property lookup Map
*/
private Map<String, DataTypeDefinition> getCustomPropertyLookup()
{
if (customPropertyLookup == null)
{
customPropertyLookup = new HashMap<String, DataTypeDefinition>(7, 1.0f);
List<CustomProperty> customProps = getClientConfig().getCustomProperties();
if (customProps != null)
{
DictionaryService dd = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getDictionaryService();
for (CustomProperty customProp : customProps)
{
PropertyDefinition propDef = null;
QName propQName = Repository.resolveToQName(customProp.Property);
if (customProp.Type != null)
{
QName type = Repository.resolveToQName(customProp.Type);
TypeDefinition typeDef = dd.getType(type);
propDef = typeDef.getProperties().get(propQName);
}
else if (customProp.Aspect != null)
{
QName aspect = Repository.resolveToQName(customProp.Aspect);
AspectDefinition aspectDef = dd.getAspect(aspect);
propDef = aspectDef.getProperties().get(propQName);
}
customPropertyLookup.put(propQName.toString(), propDef.getDataType());
}
}
}
return customPropertyLookup;
}
/**
* Save the state of the progressive panel that was expanded/collapsed
*/
public void expandPanel(ActionEvent event)
{
if (event instanceof ExpandedEvent)
{
this.panels.put(event.getComponent().getId(), ((ExpandedEvent)event).State);
}
}
// ------------------------------------------------------------------------------
// Private data
private static final String MSG_CONTENT = "content";
private static final String MSG_ALL_FORMATS = "all_formats";
private static final String MODE_ALL = "all";
private static final String MODE_FILES_TEXT = "files_text";
private static final String MODE_FILES = "files";
private static final String MODE_FOLDERS = "folders";
private static final String LOOKIN_ALL = "all";
private static final String LOOKIN_OTHER = "other";
/** The NodeService to be used by the bean */
private NodeService nodeService;
/** The NamespaceService to be used by the bean */
private NamespaceService namespaceService;
/** The NavigationBean reference */
private NavigationBean navigator;
/** Client Config reference */
private ClientConfigElement clientConfigElement = null;
/** Progressive panel UI state */
private Map<String, Boolean> panels = new HashMap(5, 1.0f);
/** custom property names to values */
private Map<String, Object> customProperties = new HashMap(5, 1.0f);
/** lookup of custom property QName string to DataTypeDefinition for the property */
private Map<String, DataTypeDefinition> customPropertyLookup = null;
/** content types to for restricting searches */
private List<SelectItem> contentTypes;
/** content format list restricting searches */
private List<SelectItem> contentFormats;
/** content type selection */
private String contentType;
/** content format selection */
private String contentFormat;
/** the text to search for */
private String text = "";
/** search mode */
private String mode = MODE_ALL;
/** folder lookin mode */
private String lookin = LOOKIN_ALL;
/** Space Selector location */
private NodeRef location = null;
/** categories to search */
private NodeRef category = null;
/** title attribute to search */
private String title = null;
/** description attribute to search */
private String description = null;
/** created attribute to search from */
private Date createdDateFrom = null;
/** created attribute to search to */
private Date createdDateTo = null;
/** modified attribute to search from */
private Date modifiedDateFrom = null;
/** modified attribute to search to */
private Date modifiedDateTo = null;
/** true to search location children as well as location */
private boolean locationChildren = true;
/** true to search category children as well as category */
private boolean categoryChildren = true;
/** author (creator) attribute to search */
private String author = null;
private boolean modifiedDateChecked = false;
private boolean createdDateChecked = false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,688 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.CategoryService;
import org.alfresco.service.cmr.search.CategoryService.Depth;
import org.alfresco.service.cmr.search.CategoryService.Mode;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.IBreadcrumbHandler;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.UIBreadcrumb;
import org.alfresco.web.ui.common.component.UIModeList;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.alfresco.web.ui.repo.component.IRepoBreadcrumbHandler;
import org.apache.log4j.Logger;
/**
* Backing Bean for the Category Management pages.
*
* @author Kevin Roast
*/
public class CategoriesBean implements IContextListener
{
private static final String DEFAULT_OUTCOME = "finish";
private static final String MSG_CATEGORIES = "categories";
private static Logger logger = Logger.getLogger(CategoriesBean.class);
/** The NodeService to be used by the bean */
private NodeService nodeService;
private CategoryService categoryService;
/** Component references */
private UIRichList categoriesRichList;
/** Currently visible category Node*/
private Node category = null;
/** Current category ref */
private NodeRef categoryRef = null;
/** Action category node */
private Node actionCategory = null;
/** Member Count of the linked items of a category */
private Integer members = null;
/** Dialog properties */
private String name = null;
private String description = null;
/** RichList view mode */
private String viewMode = "icons";
/** Category path breadcrumb location */
private List<IBreadcrumbHandler> location = null;
// ------------------------------------------------------------------------------
// Construction
/**
* Default Constructor
*/
public CategoriesBean()
{
UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
}
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param categoryService The CategoryService to set.
*/
public void setCategoryService(CategoryService categoryService)
{
this.categoryService = categoryService;
}
/**
* @param list The categories RichList to set.
*/
public void setCategoriesRichList(UIRichList list)
{
this.categoriesRichList = list;
}
/**
* @return Returns the categories RichList to set.
*/
public UIRichList getCategoriesRichList()
{
return this.categoriesRichList;
}
/**
* @return Returns the description.
*/
public String getDescription()
{
return this.description;
}
/**
* @param description The description to set.
*/
public void setDescription(String description)
{
this.description = description;
}
/**
* @return Returns the viewMode.
*/
public String getViewMode()
{
return this.viewMode;
}
/**
* @param viewMode The viewMode to set.
*/
public void setViewMode(String viewMode)
{
this.viewMode = viewMode;
}
/**
* @return Returns the name.
*/
public String getName()
{
return this.name;
}
/**
* @param name The name to set.
*/
public void setName(String name)
{
this.name = name;
}
/**
* @return Returns the members count for current action category.
*/
public int getMembers()
{
return (this.members != null ? this.members.intValue() : 0);
}
/**
* @return Returns the Node being used for the current action screen.
*/
public Node getActionCategory()
{
return this.actionCategory;
}
/**
* @param actionSpace Set the Node to be used for the current category screen action.
*/
public void setActionCategory(Node node)
{
this.actionCategory = node;
if (node != null)
{
// setup form properties
this.name = node.getName();
this.description = (String)node.getProperties().get(ContentModel.PROP_DESCRIPTION);
this.members = this.categoryService.getChildren(node.getNodeRef(), Mode.MEMBERS, Depth.ANY).size();
}
else
{
this.name = null;
this.description = null;
this.members = 0;
}
}
/**
* @return The currently displayed category as a Node or null if at the root.
*/
public Node getCurrentCategory()
{
if (this.category == null)
{
if (this.categoryRef != null)
{
this.category = new Node(this.categoryRef);
}
}
return this.category;
}
/**
* @return The ID of the currently displayed category or null if at the root.
*/
public String getCurrentCategoryId()
{
if (this.categoryRef != null)
{
return categoryRef.getId();
}
else
{
return null;
}
}
/**
* Set the current category node.
* <p>
* Setting this value causes the UI to update and display the specified node as current.
*
* @param ref The current category node.
*/
public void setCurrentCategory(NodeRef ref)
{
if (logger.isDebugEnabled())
logger.debug("Setting current category: " + ref);
// set the current NodeRef for our UI context operations
this.categoryRef = ref;
// clear current node context
this.category = null;
// inform that the UI needs updating after this change
contextUpdated();
}
/**
* @return Breadcrumb location list
*/
public List<IBreadcrumbHandler> getLocation()
{
if (this.location == null)
{
List<IBreadcrumbHandler> loc = new ArrayList<IBreadcrumbHandler>(8);
loc.add(new CategoryBreadcrumbHandler(null,
Application.getMessage(FacesContext.getCurrentInstance(), MSG_CATEGORIES)));
this.location = loc;
}
return this.location;
}
/**
* @param location Breadcrumb location list
*/
public void setLocation(List<IBreadcrumbHandler> location)
{
this.location = location;
}
/**
* @return The list of categories Nodes to display. Returns the list root categories or the
* list of sub-categories for the current category if set.
*/
public List<Node> getCategories()
{
List<Node> categories;
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context, true);
tx.begin();
Collection<ChildAssociationRef> refs;
if (this.categoryRef == null)
{
// root categories
refs = this.categoryService.getCategories(Repository.getStoreRef(), ContentModel.ASPECT_GEN_CLASSIFIABLE, Depth.IMMEDIATE);
}
else
{
// sub-categories of an existing category
refs = this.categoryService.getChildren(this.categoryRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE);
}
categories = new ArrayList<Node>(refs.size());
for (ChildAssociationRef child : refs)
{
Node categoryNode = new Node(child.getChildRef());
// force early props init within transaction
categoryNode.getProperties();
categories.add(categoryNode);
}
// commit the transaction
tx.commit();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {refErr.getNodeRef()}) );
categories = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
categories = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
return categories;
}
/**
* Set the Category to be used for next action dialog
*/
public void setupCategoryAction(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
if (logger.isDebugEnabled())
logger.debug("Setup for action, setting current Category to: " + id);
try
{
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
Node node = new Node(ref);
// prepare a node for the action context
setActionCategory(node);
// clear datalist cache ready from return from action dialog
contextUpdated();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
}
/**
* Clear the category action context - e.g. ready for a Create operation
*/
public void clearCategoryAction(ActionEvent event)
{
setActionCategory(null);
// clear datalist cache ready from return from action dialog
contextUpdated();
}
/**
* Action called when a category folder is clicked.
* Navigate into the category.
*/
public void clickCategory(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
try
{
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
// refresh UI based on node selection
updateUILocation(ref);
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
}
/**
* Action handler called on Create Category finish button click.
*/
public String finishCreate()
{
String outcome = DEFAULT_OUTCOME;
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// create category using categoryservice
NodeRef ref;
if (categoryRef == null)
{
ref = this.categoryService.createRootCategory(Repository.getStoreRef(), ContentModel.ASPECT_GEN_CLASSIFIABLE, this.name);
}
else
{
ref = this.categoryService.createCategory(categoryRef, this.name);
}
// apply the titled aspect - for description
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>(1, 1.0f);
titledProps.put(ContentModel.PROP_DESCRIPTION, this.description);
this.nodeService.addAspect(ref, ContentModel.ASPECT_TITLED, titledProps);
// commit the transaction
tx.commit();
}
catch (Throwable err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
outcome = null;
}
return outcome;
}
/**
* Action handler called on Edit Category finish button click.
*/
public String finishEdit()
{
String outcome = DEFAULT_OUTCOME;
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// update the category node
NodeRef nodeRef = getActionCategory().getNodeRef();
this.nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, this.name);
// apply the titled aspect - for description
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TITLED) == false)
{
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>(1, 1.0f);
titledProps.put(ContentModel.PROP_DESCRIPTION, this.description);
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_TITLED, titledProps);
}
else
{
this.nodeService.setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, this.description);
}
// commit the transaction
tx.commit();
// edit the node in the breadcrumb if required
List<IBreadcrumbHandler> location = getLocation();
IBreadcrumbHandler handler = location.get(location.size() - 1);
// see if the current breadcrumb location is our node
if ( nodeRef.equals(((IRepoBreadcrumbHandler)handler).getNodeRef()) )
{
// and update with the modified node details
IBreadcrumbHandler newHandler = new CategoryBreadcrumbHandler(
nodeRef, Repository.getNameForNode(nodeService, nodeRef));
location.set(location.size() - 1, newHandler);
}
}
catch (Throwable err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
outcome = null;
}
return outcome;
}
/**
* Action handler called on Delete Category finish button click.
*/
public String finishDelete()
{
String outcome = DEFAULT_OUTCOME;
if (getActionCategory() != null)
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// delete the category node using the nodeservice
NodeRef nodeRef = getActionCategory().getNodeRef();
this.categoryService.deleteCategory(nodeRef);
// commit the transaction
tx.commit();
// remove this node from the breadcrumb if required
List<IBreadcrumbHandler> location = getLocation();
IBreadcrumbHandler handler = location.get(location.size() - 1);
// see if the current breadcrumb location is our node
if ( nodeRef.equals(((IRepoBreadcrumbHandler)handler).getNodeRef()) )
{
location.remove(location.size() - 1);
// now work out which node to set the list to refresh against
if (location.size() != 0)
{
handler = location.get(location.size() - 1);
this.setCurrentCategory(((IRepoBreadcrumbHandler)handler).getNodeRef());
}
}
// clear action context
setActionCategory(null);
}
catch (Throwable err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
outcome = null;
}
}
return outcome;
}
/**
* Change the current view mode based on user selection
*
* @param event ActionEvent
*/
public void viewModeChanged(ActionEvent event)
{
UIModeList viewList = (UIModeList)event.getComponent();
// get the view mode ID
setViewMode(viewList.getValue().toString());
}
/**
* Update the breadcrumb with the clicked category location
*/
private void updateUILocation(NodeRef ref)
{
String name = Repository.getNameForNode(this.nodeService, ref);
this.location.add(new CategoryBreadcrumbHandler(ref, name));
this.setCurrentCategory(ref);
}
// ------------------------------------------------------------------------------
// IContextListener implementation
/**
* @see org.alfresco.web.app.context.IContextListener#contextUpdated()
*/
public void contextUpdated()
{
if (logger.isDebugEnabled())
logger.debug("Invalidating Category Management Components...");
// force a requery of the current category ref properties
this.category = null;
// force a requery of the richlist dataset
this.categoriesRichList.setValue(null);
}
// ------------------------------------------------------------------------------
// Inner classes
/**
* Class to handle breadcrumb interaction for Categories pages
*/
private class CategoryBreadcrumbHandler implements IRepoBreadcrumbHandler
{
private static final long serialVersionUID = 3831234653171036630L;
/**
* Constructor
*
* @param NodeRef The NodeRef for this browse navigation element
* @param label Element label
*/
public CategoryBreadcrumbHandler(NodeRef nodeRef, String label)
{
this.label = label;
this.nodeRef = nodeRef;
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
return this.label;
}
/**
* @see org.alfresco.web.ui.common.component.IBreadcrumbHandler#navigationOutcome(org.alfresco.web.ui.common.component.UIBreadcrumb)
*/
public String navigationOutcome(UIBreadcrumb breadcrumb)
{
// All category breadcrumb elements relate to a Categiry Node Id
// when selected we set the current category Id and return
setCurrentCategory(this.nodeRef);
setLocation( (List)breadcrumb.getValue() );
return null;
}
public NodeRef getNodeRef()
{
return this.nodeRef;
}
private NodeRef nodeRef;
private String label;
}
}

View File

@@ -0,0 +1,951 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.File;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionType;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.app.servlet.DownloadContentServlet;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Kevin Roast
*/
public class CheckinCheckoutBean
{
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @return Returns the BrowseBean.
*/
public BrowseBean getBrowseBean()
{
return this.browseBean;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @return Returns the NodeService.
*/
public NodeService getNodeService()
{
return this.nodeService;
}
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @return Returns the VersionOperationsService.
*/
public CheckOutCheckInService getVersionOperationsService()
{
return this.versionOperationsService;
}
/**
* @param versionOperationsService The VersionOperationsService to set.
*/
public void setVersionOperationsService(CheckOutCheckInService versionOperationsService)
{
this.versionOperationsService = versionOperationsService;
}
/**
* @return Returns the ContentService.
*/
public ContentService getContentService()
{
return this.contentService;
}
/**
* @param contentService The ContentService to set.
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* @return The document node being used for the current operation
*/
public Node getDocument()
{
return this.document;
}
/**
* @param document The document node to be used for the current operation
*/
public void setDocument(Node document)
{
this.document = document;
}
/**
* @return Returns the working copy Document.
*/
public Node getWorkingDocument()
{
return this.workingDocument;
}
/**
* @param workingDocument The working copy Document to set.
*/
public void setWorkingDocument(Node workingDocument)
{
this.workingDocument = workingDocument;
}
/**
* Determines whether the document being checked in has
* the versionable aspect applied
*
* @return true if the versionable aspect is applied
*/
public boolean isVersionable()
{
return getDocument().hasAspect(ContentModel.ASPECT_VERSIONABLE);
}
/**
* @param keepCheckedOut The keepCheckedOut to set.
*/
public void setKeepCheckedOut(boolean keepCheckedOut)
{
this.keepCheckedOut = keepCheckedOut;
}
/**
* @return Returns the keepCheckedOut.
*/
public boolean getKeepCheckedOut()
{
return this.keepCheckedOut;
}
/**
* @param minorChange The minorChange to set.
*/
public void setMinorChange(boolean minorChange)
{
this.minorChange = minorChange;
}
/**
* @return Returns the minorChange flag.
*/
public boolean getMinorChange()
{
return this.minorChange;
}
/**
* @return Returns the version history notes.
*/
public String getVersionNotes()
{
return this.versionNotes;
}
/**
* @param versionNotes The version history notes to set.
*/
public void setVersionNotes(String versionNotes)
{
this.versionNotes = versionNotes;
}
/**
* @return Returns the selected Space Id.
*/
public NodeRef getSelectedSpaceId()
{
return this.selectedSpaceId;
}
/**
* @param selectedSpaceId The selected Space Id to set.
*/
public void setSelectedSpaceId(NodeRef selectedSpaceId)
{
this.selectedSpaceId = selectedSpaceId;
}
/**
* @return Returns the copy location. Either the current or other space.
*/
public String getCopyLocation()
{
if (this.fileName != null)
{
return CheckinCheckoutBean.COPYLOCATION_OTHER;
}
else
{
return this.copyLocation;
}
}
/**
* @param copyLocation The copy location. Either the current or other space.
*/
public void setCopyLocation(String copyLocation)
{
this.copyLocation = copyLocation;
}
/**
* @return Returns the message to display when a file has been uploaded
*/
public String getFileUploadSuccessMsg()
{
String msg = Application.getMessage(FacesContext.getCurrentInstance(), "file_upload_success");
return MessageFormat.format(msg, new Object[] {getFileName()});
}
/**
* @return Returns the name of the file
*/
public String getFileName()
{
// try and retrieve the file and filename from the file upload bean
// representing the file we previously uploaded.
FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
this.file = fileBean.getFile();
this.fileName = fileBean.getFileName();
}
return this.fileName;
}
/**
* @param fileName The name of the file
*/
public void setFileName(String fileName)
{
this.fileName = fileName;
// we also need to keep the file upload bean in sync
FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
fileBean.setFileName(this.fileName);
}
}
/**
* @return Returns the document content used for HTML in-line editing.
*/
public String getDocumentContent()
{
return this.documentContent;
}
/**
* @param documentContent The document content for HTML in-line editing.
*/
public void setDocumentContent(String documentContent)
{
this.documentContent = documentContent;
}
/**
* @return Returns output from the in-line editor page.
*/
public String getEditorOutput()
{
return this.editorOutput;
}
/**
* @param editorOutput The output from the in-line editor page
*/
public void setEditorOutput(String editorOutput)
{
this.editorOutput = editorOutput;
}
// ------------------------------------------------------------------------------
// Navigation action event handlers
/**
* Action event called by all actions that need to setup a Content Document context on the
* CheckinCheckoutBean before an action page/wizard is called. The context will be a Node in
* setDocument() which can be retrieved on action pages via getDocument().
*
* @param event ActionEvent
*/
public void setupContentAction(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
setupContentDocument(id);
}
else
{
setDocument(null);
}
clearUpload();
}
/**
* Setup a content document node context
*
* @param id GUID of the node to setup as the content document context
*
* @return The Node
*/
private Node setupContentDocument(String id)
{
if (logger.isDebugEnabled())
logger.debug("Setup for action, setting current document to: " + id);
Node node = null;
try
{
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
node = new Node(ref);
// create content URL to the content download servlet with ID and expected filename
// the myfile part will be ignored by the servlet but gives the browser a hint
String url = DownloadContentServlet.generateDownloadURL(ref, node.getName());
node.getProperties().put("url", url);
node.getProperties().put("workingCopy", node.hasAspect(ContentModel.ASPECT_WORKING_COPY));
node.getProperties().put("fileType32", Utils.getFileTypeImage(node.getName(), false));
// remember the document
setDocument(node);
// refresh the UI, calling this method now is fine as it basically makes sure certain
// beans clear the state - so when we finish here other beans will have been reset
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
return node;
}
/**
* Action called upon completion of the Check Out file page
*/
public String checkoutFile()
{
String outcome = null;
UserTransaction tx = null;
Node node = getDocument();
if (node != null)
{
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
if (logger.isDebugEnabled())
logger.debug("Trying to checkout content node Id: " + node.getId());
// checkout the node content to create a working copy
if (logger.isDebugEnabled())
{
logger.debug("Checkout copy location: " + getCopyLocation());
logger.debug("Selected Space Id: " + this.selectedSpaceId);
}
NodeRef workingCopyRef;
if (getCopyLocation().equals(COPYLOCATION_OTHER) && this.selectedSpaceId != null)
{
// checkout to a arbituary parent Space
NodeRef destRef = this.selectedSpaceId;
ChildAssociationRef childAssocRef = this.nodeService.getPrimaryParent(destRef);
workingCopyRef = this.versionOperationsService.checkout(node.getNodeRef(),
destRef, ContentModel.ASSOC_CONTAINS, childAssocRef.getQName());
}
else
{
workingCopyRef = this.versionOperationsService.checkout(node.getNodeRef());
}
// set the working copy Node instance
Node workingCopy = new Node(workingCopyRef);
setWorkingDocument(workingCopy);
// create content URL to the content download servlet with ID and expected filename
// the myfile part will be ignored by the servlet but gives the browser a hint
String url = DownloadContentServlet.generateDownloadURL(workingCopyRef, workingCopy.getName());
workingCopy.getProperties().put("url", url);
workingCopy.getProperties().put("fileType32", Utils.getFileTypeImage(workingCopy.getName(), false));
// commit the transaction
tx.commit();
// show the page that display the checkout link
outcome = "checkoutFileLink";
}
catch (Throwable err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_CHECKOUT) + err.getMessage(), err);
}
}
else
{
logger.warn("WARNING: checkoutFile called without a current Document!");
}
return outcome;
}
/**
* Action called upon completion of the Check Out file Link download page
*/
public String checkoutFileOK()
{
String outcome = null;
Node node = getWorkingDocument();
if (node != null)
{
// clean up and clear action context
clearUpload();
setDocument(null);
setWorkingDocument(null);
outcome = "browse";
}
else
{
logger.warn("WARNING: checkoutFileOK called without a current WorkingDocument!");
}
return outcome;
}
/**
* Action called upon completion of the Edit File download page
*/
public String editFileOK()
{
String outcome = null;
Node node = getDocument();
if (node != null)
{
// clean up and clear action context
clearUpload();
setDocument(null);
setWorkingDocument(null);
outcome = "browse";
}
else
{
logger.warn("WARNING: editFileOK called without a current Document!");
}
return outcome;
}
/**
* Action handler called to calculate which editing screen to display based on the mimetype
* of a document. If appropriate, the in-line editing screen will be shown.
*/
public void editFile(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
Node node = setupContentDocument(id);
// detect the inline editing aspect to see which edit mode to use
if (node.hasAspect(ContentModel.ASPECT_INLINEEDITABLE) &&
node.getProperties().get(ContentModel.PROP_EDITINLINE) != null &&
((Boolean)node.getProperties().get(ContentModel.PROP_EDITINLINE)).booleanValue() == true)
{
// retrieve the content reader for this node
ContentReader reader = getContentService().getReader(node.getNodeRef(), ContentModel.PROP_CONTENT);
String mimetype = reader.getMimetype();
// calculate which editor screen to display
if (MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(mimetype) ||
MimetypeMap.MIMETYPE_XML.equals(mimetype) ||
MimetypeMap.MIMETYPE_TEXT_CSS.equals(mimetype))
{
// make content available to the editing screen
if (reader != null)
{
setEditorOutput(reader.getContentString());
}
else
{
setEditorOutput("");
}
// navigate to appropriate screen
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "editTextInline");
}
else
{
// make content available to the editing screen
if (reader != null)
{
setDocumentContent(reader.getContentString());
}
else
{
setDocumentContent("");
}
setEditorOutput(null);
// navigate to appropriate screen
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "editHtmlInline");
}
}
else
{
// normal downloadable document
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "editFile");
}
}
}
/**
* Action handler called to set the content of a node from an inline editing page.
*/
public String editInlineOK()
{
String outcome = null;
UserTransaction tx = null;
Node node = getDocument();
if (node != null)
{
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
if (logger.isDebugEnabled())
logger.debug("Trying to update content node Id: " + node.getId());
// get an updating writer that we can use to modify the content on the current node
ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true);
writer.putContent(this.editorOutput);
// commit the transaction
tx.commit();
// clean up and clear action context
clearUpload();
setDocument(null);
setDocumentContent(null);
setEditorOutput(null);
outcome = "browse";
}
catch (Throwable err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_UPDATE) + err.getMessage());
}
}
else
{
logger.warn("WARNING: editInlineOK called without a current Document!");
}
return outcome;
}
/**
* Action to undo the checkout of a document just checked out from the checkout screen.
*/
public String undoCheckout()
{
String outcome = null;
Node node = getWorkingDocument();
if (node != null)
{
try
{
// try to cancel checkout of the working copy
this.versionOperationsService.cancelCheckout(node.getNodeRef());
clearUpload();
outcome = "browse";
}
catch (Throwable err)
{
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_CANCELCHECKOUT) + err.getMessage(), err);
}
}
else
{
logger.warn("WARNING: undoCheckout called without a current WorkingDocument!");
}
return outcome;
}
/**
* Action to undo the checkout of a locked document. This document may either by the original copy
* or the working copy node. Therefore calculate which it is, if the working copy is found then
* we simply cancel checkout on that document. If the original copy is found then we need to find
* the appropriate working copy and perform the action on that node.
*/
public String undoCheckoutFile()
{
String outcome = null;
Node node = getDocument();
if (node != null)
{
try
{
if (node.hasAspect(ContentModel.ASPECT_WORKING_COPY))
{
this.versionOperationsService.cancelCheckout(node.getNodeRef());
}
else if (node.hasAspect(ContentModel.ASPECT_LOCKABLE))
{
// TODO: find the working copy for this document and cancel the checkout on it
// is this possible? as currently only the workingcopy aspect has the copyReference
// attribute - this means we cannot find out where the copy is to cancel it!
// can we construct an XPath node lookup?
throw new RuntimeException("NOT IMPLEMENTED");
}
else
{
throw new IllegalStateException("Node supplied for undo checkout has neither Working Copy or Locked aspect!");
}
clearUpload();
outcome = "browse";
}
catch (Throwable err)
{
Utils.addErrorMessage(MSG_ERROR_CANCELCHECKOUT + err.getMessage(), err);
}
}
else
{
logger.warn("WARNING: undoCheckout called without a current WorkingDocument!");
}
return outcome;
}
/**
* Action called upon completion of the Check In file page
*/
public String checkinFileOK()
{
String outcome = null;
UserTransaction tx = null;
// NOTE: for checkin the document node _is_ the working document!
Node node = getDocument();
if (node != null && (getCopyLocation().equals(COPYLOCATION_CURRENT) || this.getFileName() != null))
{
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
if (logger.isDebugEnabled())
logger.debug("Trying to checkin content node Id: " + node.getId());
// we can either checkin the content from the current working copy node
// which would have been previously updated by the user
String contentUrl;
String mimetype;
if (getCopyLocation().equals(COPYLOCATION_CURRENT))
{
ContentData contentData = (ContentData) node.getProperties().get(ContentModel.PROP_CONTENT);
contentUrl = (contentData == null ? null : contentData.getContentUrl());
}
// or specify a specific file as the content instead
else
{
// add the content to an anonymous but permanent writer location
// we can then retrieve the URL to the content to to be set on the node during checkin
ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, false);
// TODO: Adjust the mimetype
writer.putContent(this.file);
contentUrl = writer.getContentUrl();
}
if (contentUrl == null || contentUrl.length() == 0)
{
throw new IllegalStateException("Content URL is empty for specified working copy content node!");
}
// add version history text to props
Map<String, Serializable> props = new HashMap<String, Serializable>(1, 1.0f);
props.put(Version.PROP_DESCRIPTION, this.versionNotes);
// set the flag for minor or major change
if (this.minorChange)
{
props.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR);
}
else
{
props.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR);
}
NodeRef originalDoc = this.versionOperationsService.checkin(
node.getNodeRef(),
props,
contentUrl,
this.keepCheckedOut);
// commit the transaction
tx.commit();
// clear action context
setDocument(null);
clearUpload();
outcome = "browse";
}
catch (Throwable err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_CHECKIN) + err.getMessage(), err);
}
}
else
{
logger.warn("WARNING: checkinFileOK called without a current Document!");
}
return outcome;
}
/**
* Action called upon completion of the Update File page
*/
public String updateFileOK()
{
String outcome = null;
UserTransaction tx = null;
// NOTE: for update the document node _is_ the working document!
Node node = getDocument();
if (node != null && this.getFileName() != null)
{
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
if (logger.isDebugEnabled())
logger.debug("Trying to update content node Id: " + node.getId());
// get an updating writer that we can use to modify the content on the current node
ContentWriter writer = this.contentService.getWriter(node.getNodeRef(), ContentModel.PROP_CONTENT, true);
writer.putContent(this.file);
// commit the transaction
tx.commit();
// clear action context
setDocument(null);
clearUpload();
outcome = "browse";
}
catch (Throwable err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_UPDATE) + err.getMessage(), err);
}
}
else
{
logger.warn("WARNING: updateFileOK called without a current Document!");
}
return outcome;
}
/**
* Deals with the cancel button being pressed on the check in file page
*/
public String cancel()
{
// reset the state
clearUpload();
return "browse";
}
/**
* Clear form state and upload file bean
*/
private void clearUpload()
{
// delete the temporary file we uploaded earlier
if (this.file != null)
{
this.file.delete();
}
this.file = null;
this.fileName = null;
this.keepCheckedOut = false;
this.minorChange = true;
this.copyLocation = COPYLOCATION_CURRENT;
this.versionNotes = "";
this.selectedSpaceId = null;
// remove the file upload bean from the session
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.getExternalContext().getSessionMap().remove(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
}
// ------------------------------------------------------------------------------
// Private data
private static Log logger = LogFactory.getLog(CheckinCheckoutBean.class);
/** I18N messages */
private static final String MSG_ERROR_CHECKIN = "error_checkin";
private static final String MSG_ERROR_CANCELCHECKOUT = "error_cancel_checkout";
private static final String MSG_ERROR_UPDATE = "error_update";
private static final String MSG_ERROR_CHECKOUT = "error_checkout";
/** constants for copy location selection */
private static final String COPYLOCATION_CURRENT = "current";
private static final String COPYLOCATION_OTHER = "other";
/** The current document */
private Node document;
/** The working copy of the document we are checking out */
private Node workingDocument;
/** Content of the document used for HTML in-line editing */
private String documentContent;
/** Content of the document returned from in-line editing */
private String editorOutput;
/** transient form and upload properties */
private File file;
private String fileName;
private boolean keepCheckedOut = false;
private boolean minorChange = true;
private String copyLocation = COPYLOCATION_CURRENT;
private String versionNotes = "";
private NodeRef selectedSpaceId = null;
/** The BrowseBean to be used by the bean */
private BrowseBean browseBean;
/** The NodeService to be used by the bean */
private NodeService nodeService;
/** The VersionOperationsService to be used by the bean */
private CheckOutCheckInService versionOperationsService;
/** The ContentService to be used by the bean */
private ContentService contentService;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,353 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import javax.transaction.UserTransaction;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigLookupContext;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.config.PropertySheetConfigElement;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.alfresco.web.ui.common.Utils;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Backing bean for the edit document properties dialog
*
* @author gavinc
*/
public class DocumentPropertiesBean
{
private static final String TEMP_PROP_MIMETYPE = "mimetype";
private NodeService nodeService;
private FileFolderService fileFolderService;
private DictionaryService dictionaryService;
private BrowseBean browseBean;
private List<SelectItem> contentTypes;
private Node editableNode;
private Boolean hasOtherProperties;
/**
* Returns the node being edited
*
* @return The node being edited
*/
public Node getEditableNode()
{
return this.editableNode;
}
/**
* Event handler called to setup the document for property editing
*
* @param event The event
*/
public void setupDocumentForAction(ActionEvent event)
{
this.editableNode = new Node(this.browseBean.getDocument().getNodeRef());
// special case for Mimetype - since this is a sub-property of the ContentData object
// we must extract it so it can be edited in the client, then we check for it later
// and create a new ContentData object to wrap it and it's associated URL
ContentData content = (ContentData)this.editableNode.getProperties().get(ContentModel.PROP_CONTENT);
if (content != null)
{
this.editableNode.getProperties().put(TEMP_PROP_MIMETYPE, content.getMimetype());
}
this.hasOtherProperties = null;
}
/**
* Event handler used to save the edited properties back to the repository
*
* @return The outcome
*/
public String save()
{
String outcome = "cancel";
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
NodeRef nodeRef = this.browseBean.getDocument().getNodeRef();
Map<String, Object> props = this.editableNode.getProperties();
// get the name and move the node as necessary
String name = (String) props.get(ContentModel.PROP_NAME);
if (name != null)
{
fileFolderService.rename(nodeRef, name);
}
Map<QName, Serializable> properties = this.nodeService.getProperties(nodeRef);
// we need to put all the properties from the editable bag back into
// the format expected by the repository
// but first extract and deal with the special mimetype property for ContentData
String mimetype = (String)props.get(TEMP_PROP_MIMETYPE);
if (mimetype != null)
{
// remove temporary prop from list so it isn't saved with the others
props.remove(TEMP_PROP_MIMETYPE);
ContentData contentData = (ContentData)props.get(ContentModel.PROP_CONTENT);
if (contentData != null)
{
contentData = ContentData.setMimetype(contentData, mimetype);
props.put(ContentModel.PROP_CONTENT.toString(), contentData);
}
}
// add the remaining properties
Iterator<String> iterProps = props.keySet().iterator();
while (iterProps.hasNext())
{
String propName = iterProps.next();
QName qname = QName.createQName(propName);
// make sure the property is represented correctly
Serializable propValue = (Serializable)props.get(propName);
PropertyDefinition propDef = this.dictionaryService.getProperty(qname);
properties.put(qname, propValue);
}
// send the properties back to the repository
this.nodeService.setProperties(this.browseBean.getDocument().getNodeRef(), properties);
// we also need to persist any association changes that may have been made
// add any associations added in the UI
Map<String, Map<String, AssociationRef>> addedAssocs = this.editableNode.getAddedAssociations();
for (Map<String, AssociationRef> typedAssoc : addedAssocs.values())
{
for (AssociationRef assoc : typedAssoc.values())
{
this.nodeService.createAssociation(assoc.getSourceRef(), assoc.getTargetRef(), assoc.getTypeQName());
}
}
// remove any association removed in the UI
Map<String, Map<String, AssociationRef>> removedAssocs = this.editableNode.getRemovedAssociations();
for (Map<String, AssociationRef> typedAssoc : removedAssocs.values())
{
for (AssociationRef assoc : typedAssoc.values())
{
this.nodeService.removeAssociation(assoc.getSourceRef(), assoc.getTargetRef(), assoc.getTypeQName());
}
}
// add any child associations added in the UI
Map<String, Map<String, ChildAssociationRef>> addedChildAssocs = this.editableNode.getAddedChildAssociations();
for (Map<String, ChildAssociationRef> typedAssoc : addedChildAssocs.values())
{
for (ChildAssociationRef assoc : typedAssoc.values())
{
this.nodeService.addChild(assoc.getParentRef(), assoc.getChildRef(), assoc.getTypeQName(), assoc.getTypeQName());
}
}
// remove any child association removed in the UI
Map<String, Map<String, ChildAssociationRef>> removedChildAssocs = this.editableNode.getRemovedChildAssociations();
for (Map<String, ChildAssociationRef> typedAssoc : removedChildAssocs.values())
{
for (ChildAssociationRef assoc : typedAssoc.values())
{
this.nodeService.removeChild(assoc.getParentRef(), assoc.getChildRef());
}
}
// commit the transaction
tx.commit();
// set the outcome to refresh
outcome = "finish";
// reset the document held by the browse bean as it's just been updated
this.browseBean.getDocument().reset();
}
catch (FileExistsException e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
// print status message
String statusMsg = MessageFormat.format(
Application.getMessage(
FacesContext.getCurrentInstance(), "error_exists"),
e.getExisting().getName());
Utils.addErrorMessage(statusMsg);
// no outcome
outcome = null;
}
catch (InvalidNodeRefException err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {this.browseBean.getDocument().getId()}) );
// this failure means the node no longer exists - we cannot show the doc properties screen
outcome = "browse";
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
}
return outcome;
}
public Map<String, Object> getProperties()
{
return this.editableNode.getProperties();
}
/**
* @return Returns a list of content types to allow the user to select from
*/
public List<SelectItem> getContentTypes()
{
if (this.contentTypes == null)
{
this.contentTypes = new ArrayList<SelectItem>(80);
ServiceRegistry registry = Repository.getServiceRegistry(FacesContext.getCurrentInstance());
MimetypeService mimetypeService = registry.getMimetypeService();
// get the mime type display names
Map<String, String> mimeTypes = mimetypeService.getDisplaysByMimetype();
for (String mimeType : mimeTypes.keySet())
{
this.contentTypes.add(new SelectItem(mimeType, mimeTypes.get(mimeType)));
}
// make sure the list is sorted by the values
QuickSort sorter = new QuickSort(this.contentTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
return this.contentTypes;
}
/**
* Determines whether this document has any other properties other than the
* default set to display to the user.
*
* @return true of there are properties to show, false otherwise
*/
public boolean getOtherPropertiesPresent()
{
if (this.hasOtherProperties == null)
{
// we need to use the config service to see whether there are any
// editable properties configured for this document.
ConfigService configSvc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
Config configProps = configSvc.getConfig(this.editableNode, new ConfigLookupContext("edit-properties"));
PropertySheetConfigElement propsToDisplay = (PropertySheetConfigElement)configProps.
getConfigElement("property-sheet");
this.hasOtherProperties = Boolean.valueOf(propsToDisplay != null);
}
return this.hasOtherProperties.booleanValue();
}
/**
* @return Returns the nodeService.
*/
public NodeService getNodeService()
{
return this.nodeService;
}
/**
* @param nodeService The nodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param fileFolderService the file and folder model-specific functions
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/**
* Sets the DictionaryService to use when persisting metadata
*
* @param dictionaryService The DictionaryService
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* @return The BrowseBean
*/
public BrowseBean getBrowseBean()
{
return this.browseBean;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.ServletException;
/**
* Bean used by the error page, holds the last exception to be thrown by the system
*
* @author gavinc
*/
public class ErrorBean
{
public static final String ERROR_BEAN_NAME = "alfresco.ErrorBean";
private String returnPage;
private Throwable lastError;
/**
* @return Returns the page to go back to after the error has been displayed
*/
public String getReturnPage()
{
return returnPage;
}
/**
* @param returnPage The page to return to after showing the error
*/
public void setReturnPage(String returnPage)
{
this.returnPage = returnPage;
}
/**
* @return Returns the lastError.
*/
public Throwable getLastError()
{
return lastError;
}
/**
* @param lastError The lastError to set.
*/
public void setLastError(Throwable lastError)
{
this.lastError = lastError;
}
/**
* @return Returns the last error to occur in string form
*/
public String getLastErrorMessage()
{
String message = "No error currently stored";
if (this.lastError != null)
{
StringBuilder builder = null;
Throwable cause = null;
if (this.lastError instanceof ServletException &&
((ServletException)this.lastError).getRootCause() != null)
{
// servlet exception puts the actual error in root cause!!
Throwable actualError = ((ServletException)this.lastError).getRootCause();
builder = new StringBuilder(actualError.toString());
cause = actualError.getCause();
}
else
{
builder = new StringBuilder(this.lastError.toString());
cause = this.lastError.getCause();
}
while (cause != null)
{
builder.append("<br/><br/>caused by:<br/>");
builder.append(cause.toString());
if (cause instanceof ServletException &&
((ServletException)cause).getRootCause() != null)
{
cause = ((ServletException)cause).getRootCause();
}
else
{
cause = cause.getCause();
}
}
message = builder.toString();
}
return message;
}
/**
* @return Returns the stack trace for the last error
*/
public String getStackTrace()
{
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
if (this.lastError instanceof ServletException &&
((ServletException)this.lastError).getRootCause() != null)
{
Throwable actualError = ((ServletException)this.lastError).getRootCause();
actualError.printStackTrace(writer);
}
else
{
this.lastError.printStackTrace(writer);
}
return stringWriter.toString().replaceAll("\r\n", "<br/>");
}
}

View File

@@ -0,0 +1,330 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.transaction.UserTransaction;
import org.alfresco.repo.action.executer.ExporterActionExecuter;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Backing bean implementation for the Export dialog.
*
* @author gavinc
*/
public class ExportBean
{
private static final Log logger = LogFactory.getLog(ExportBean.class);
private static final String ALL_SPACES = "all";
private static final String CURRENT_SPACE = "current";
private static final String DEFAULT_OUTCOME = "browse";
private static final String MSG_ERROR = "error_export";
private BrowseBean browseBean;
private NodeService nodeService;
private ActionService actionService;
private String packageName;
private String encoding = "UTF-8";
private String mode = CURRENT_SPACE;
private NodeRef destination;
private boolean includeChildren = true;
private boolean runInBackground = true;
private boolean includeSelf;
/**
* Performs the export operation using the current state of the bean
*
* @return The outcome
*/
public String export()
{
if (logger.isDebugEnabled())
logger.debug("Called export for " + this.mode + " with package name: " + this.packageName);
String outcome = DEFAULT_OUTCOME;
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
// build the action params map based on the bean's current state
Map<String, Serializable> params = new HashMap<String, Serializable>(5);
params.put(ExporterActionExecuter.PARAM_STORE, Repository.getStoreRef().toString());
params.put(ExporterActionExecuter.PARAM_PACKAGE_NAME, this.packageName);
params.put(ExporterActionExecuter.PARAM_ENCODING, this.encoding);
params.put(ExporterActionExecuter.PARAM_DESTINATION_FOLDER, this.destination);
params.put(ExporterActionExecuter.PARAM_INCLUDE_CHILDREN, new Boolean(includeChildren));
params.put(ExporterActionExecuter.PARAM_INCLUDE_SELF, new Boolean(includeSelf));
// build the action to execute
Action action = this.actionService.createAction(ExporterActionExecuter.NAME, params);
action.setExecuteAsynchronously(this.runInBackground);
// get the appropriate node
NodeRef startNode = null;
if (this.mode.equals(ALL_SPACES))
{
startNode = this.nodeService.getRootNode(Repository.getStoreRef());
}
else
{
startNode = this.browseBean.getActionSpace().getNodeRef();
}
// execute the action on the relevant node
this.actionService.executeAction(action, startNode);
if (logger.isDebugEnabled())
{
logger.debug("Executed export action with action params of " + params);
}
// commit the transaction
tx.commit();
// reset the bean
reset();
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR), e.toString()), e);
outcome = null;
}
return outcome;
}
/**
* Action called when the dialog is cancelled, just resets the bean's state
*
* @return The outcome
*/
public String cancel()
{
reset();
return DEFAULT_OUTCOME;
}
/**
* Resets the dialog state back to the default
*/
public void reset()
{
this.packageName = null;
this.mode = CURRENT_SPACE;
this.destination = null;
this.includeChildren = true;
this.includeSelf = false;
this.runInBackground = true;
}
/**
* Returns the package name for the export
*
* @return The export package name
*/
public String getPackageName()
{
return this.packageName;
}
/**
* Sets the package name for the export
*
* @param packageName The export package name
*/
public void setPackageName(String packageName)
{
this.packageName = packageName;
}
/**
* The destination for the export as a NodeRef
*
* @return The destination
*/
public NodeRef getDestination()
{
return this.destination;
}
/**
* Sets the destination for the export
*
* @param destination The destination for the export
*/
public void setDestination(NodeRef destination)
{
this.destination = destination;
}
/**
* Determines whether the export will include child spaces
*
* @return true includes children
*/
public boolean getIncludeChildren()
{
return this.includeChildren;
}
/**
* Sets whether child spaces are included in the export
*
* @param includeChildren true to include the child spaces
*/
public void setIncludeChildren(boolean includeChildren)
{
this.includeChildren = includeChildren;
}
/**
* Determines whether the export will include the space itself
*
* @return true includes the space being exported from
*/
public boolean getIncludeSelf()
{
return this.includeSelf;
}
/**
* Sets whether the space itself is included in the export
*
* @param includeSelf true to include the space itself
*/
public void setIncludeSelf(boolean includeSelf)
{
this.includeSelf = includeSelf;
}
/**
* Determines whether to export only the current space or all spaces
*
* @return "all" to export all space and "current" to export the current space
*/
public String getMode()
{
return this.mode;
}
/**
* Sets whether to export the current space or all spaces
*
* @param mode "all" to export all space and "current" to export the current space
*/
public void setMode(String mode)
{
this.mode = mode;
}
/**
* Returns the encoding to use for the export
*
* @return The encoding
*/
public String getEncoding()
{
return this.encoding;
}
/**
* Sets the encoding to use for the export package
*
* @param encoding The encoding
*/
public void setEncoding(String encoding)
{
this.encoding = encoding;
}
/**
* Determines whether the import should run in the background
*
* @return true means the import will run in the background
*/
public boolean getRunInBackground()
{
return this.runInBackground;
}
/**
* Determines whether the import will run in the background
*
* @param runInBackground true to run the import in the background
*/
public void setRunInBackground(boolean runInBackground)
{
this.runInBackground = runInBackground;
}
/**
* Sets the BrowseBean instance to use to retrieve the current document
*
* @param browseBean BrowseBean instance
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* Sets the action service
*
* @param actionService the action service
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
/**
* Sets the node service
*
* @param nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.File;
/**
* Bean to hold the results of a file upload
*
* @author gavinc
*/
public final class FileUploadBean
{
public static final String FILE_UPLOAD_BEAN_NAME = "alfresco.UploadBean";
private File file;
private String fileName;
private String filePath;
/**
* @return Returns the file
*/
public File getFile()
{
return file;
}
/**
* @param file The file to set
*/
public void setFile(File file)
{
this.file = file;
}
/**
* @return Returns the name of the file uploaded
*/
public String getFileName()
{
return fileName;
}
/**
* @param fileName The name of the uploaded file
*/
public void setFileName(String fileName)
{
this.fileName = fileName;
}
/**
* @return Returns the path of the file uploaded
*/
public String getFilePath()
{
return filePath;
}
/**
* @param filePath The file path of the uploaded file
*/
public void setFilePath(String filePath)
{
this.filePath = filePath;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,322 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.File;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ImporterActionExecuter;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Backing bean implementation for the Import dialog.
*
* @author gavinc
*/
public class ImportBean
{
private static final Log logger = LogFactory.getLog(ImportBean.class);
private static final String DEFAULT_OUTCOME = "browse";
private static final String MSG_ERROR = "error_import";
private static final String MSG_ERROR_NO_FILE = "error_import_no_file";
private static final String MSG_ERROR_EMPTY_FILE = "error_import_empty_file";
private BrowseBean browseBean;
private NodeService nodeService;
private ActionService actionService;
private ContentService contentService;
private File file;
private String fileName;
private String encoding = "UTF-8";
private boolean runInBackground = true;
/**
* Performs the import operation using the current state of the bean
*
* @return The outcome
*/
public String performImport()
{
String outcome = DEFAULT_OUTCOME;
if (logger.isDebugEnabled())
logger.debug("Called import for file: " + this.file);
if (this.file != null && this.file.exists())
{
// check the file actually has contents
if (this.file.length() > 0)
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// first of all we need to add the uploaded ACP file to the repository
NodeRef acpNodeRef = addACPToRepository(context);
// build the action params map based on the bean's current state
Map<String, Serializable> params = new HashMap<String, Serializable>(3);
params.put(ImporterActionExecuter.PARAM_DESTINATION_FOLDER, this.browseBean.getActionSpace().getNodeRef());
params.put(ImporterActionExecuter.PARAM_ENCODING, this.encoding);
// build the action to execute
Action action = this.actionService.createAction(ImporterActionExecuter.NAME, params);
action.setExecuteAsynchronously(this.runInBackground);
// execute the action on the ACP file
this.actionService.executeAction(action, acpNodeRef);
if (logger.isDebugEnabled())
{
logger.debug("Executed import action with action params of " + params);
}
// commit the transaction
tx.commit();
// reset the bean
reset();
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR), e.toString()), e);
outcome = null;
}
}
else
{
Utils.addErrorMessage(Application.getMessage(FacesContext.getCurrentInstance(), MSG_ERROR_EMPTY_FILE));
outcome = null;
}
}
else
{
Utils.addErrorMessage(Application.getMessage(FacesContext.getCurrentInstance(), MSG_ERROR_NO_FILE));
outcome = null;
}
return outcome;
}
/**
* Action called when the dialog is cancelled, just resets the bean's state
*
* @return The outcome
*/
public String cancel()
{
reset();
return DEFAULT_OUTCOME;
}
/**
* Resets the dialog state back to the default
*/
public void reset()
{
this.file = null;
this.fileName = null;
this.runInBackground = true;
// delete the temporary file we uploaded earlier
if (this.file != null)
{
this.file.delete();
}
// remove the file upload bean from the session
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.getExternalContext().getSessionMap().remove(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
}
/**
* @return Returns the message to display when a file has been uploaded
*/
public String getFileUploadSuccessMsg()
{
String msg = Application.getMessage(FacesContext.getCurrentInstance(), "file_upload_success");
return MessageFormat.format(msg, new Object[] {getFileName()});
}
/**
* @return Returns the name of the file
*/
public String getFileName()
{
// try and retrieve the file and filename from the file upload bean
// representing the file we previously uploaded.
FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
this.fileName = fileBean.getFileName();
this.file = fileBean.getFile();
}
return this.fileName;
}
/**
* Returns the encoding to use for the export
*
* @return The encoding
*/
public String getEncoding()
{
return this.encoding;
}
/**
* Sets the encoding to use for the export package
*
* @param encoding The encoding
*/
public void setEncoding(String encoding)
{
this.encoding = encoding;
}
/**
* Determines whether the import should run in the background
*
* @return true means the import will run in the background
*/
public boolean getRunInBackground()
{
return this.runInBackground;
}
/**
* Determines whether the import will run in the background
*
* @param runInBackground true to run the import in the background
*/
public void setRunInBackground(boolean runInBackground)
{
this.runInBackground = runInBackground;
}
/**
* Sets the BrowseBean instance to use to retrieve the current document
*
* @param browseBean BrowseBean instance
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* Sets the action service
*
* @param actionService the action service
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
/**
* Sets the node service
*
* @param nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Sets the content service
*
* @param contentService the content service
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* Adds the uploaded ACP file to the repository
*
* @param context Faces context
* @return NodeRef representing the ACP file in the repository
*/
private NodeRef addACPToRepository(FacesContext context)
{
// set the name for the new node
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>(1);
contentProps.put(ContentModel.PROP_NAME, this.fileName);
// create the node to represent the zip file
String assocName = QName.createValidLocalName(this.fileName);
ChildAssociationRef assocRef = this.nodeService.createNode(
this.browseBean.getActionSpace().getNodeRef(), ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, assocName),
ContentModel.TYPE_CONTENT, contentProps);
NodeRef acpNodeRef = assocRef.getChildRef();
// apply the titled aspect to behave in the web client
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>(3, 1.0f);
titledProps.put(ContentModel.PROP_TITLE, this.fileName);
titledProps.put(ContentModel.PROP_DESCRIPTION, Application.getMessage(context, "import_package_description"));
this.nodeService.addAspect(acpNodeRef, ContentModel.ASPECT_TITLED, titledProps);
// add the content to the node
ContentWriter writer = this.contentService.getWriter(acpNodeRef, ContentModel.PROP_CONTENT, true);
writer.setEncoding(this.encoding);
writer.setMimetype(MimetypeMap.MIMETYPE_ACP);
writer.putContent(this.file);
return acpNodeRef;
}
}

View File

@@ -0,0 +1,565 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.faces.validator.ValidatorException;
import javax.portlet.PortletRequest;
import javax.servlet.http.HttpServletRequest;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.webdav.WebDAVServlet;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.config.ClientConfigElement;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* JSF Managed Bean. Backs the "login.jsp" view to provide the form fields used
* to enter user data for login. Also contains bean methods to validate form
* fields and action event fired in response to the Login button being pressed.
*
* @author Kevin Roast
*/
public class LoginBean
{
// ------------------------------------------------------------------------------
// Managed bean properties
/**
* @param authenticationService The AuthenticationService to set.
*/
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
/**
* @param personService The personService to set.
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* @param nodeService The nodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @param configService The ConfigService to set.
*/
public void setConfigService(ConfigService configService)
{
this.configService = configService;
}
/**
* @param fileFolderService The FileFolderService to set.
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/**
* @param val Username from login dialog
*/
public void setUsername(String val)
{
this.username = val;
}
/**
* @return The username string from login dialog
*/
public String getUsername()
{
return this.username;
}
/**
* @param val Password from login dialog
*/
public void setPassword(String val)
{
this.password = val;
}
/**
* @return The password string from login dialog
*/
public String getPassword()
{
return this.password;
}
/**
* @return the available languages
*/
public SelectItem[] getLanguages()
{
ClientConfigElement config = (ClientConfigElement) this.configService.getGlobalConfig()
.getConfigElement(ClientConfigElement.CONFIG_ELEMENT_ID);
List<String> languages = config.getLanguages();
SelectItem[] items = new SelectItem[languages.size()];
int count = 0;
for (String locale : languages)
{
// get label associated to the locale
String label = config.getLabelForLanguage(locale);
// set default selection
if (count == 0 && this.language == null)
{
// first try to get the language that the current user is using
Locale lastLocale = Application.getLanguage(FacesContext.getCurrentInstance());
if (lastLocale != null)
{
this.language = lastLocale.toString();
}
// else we default to the first item in the list
else
{
this.language = locale;
}
}
items[count++] = new SelectItem(locale, label);
}
return items;
}
/**
* @return Returns the language selection.
*/
public String getLanguage()
{
return this.language;
}
/**
* @param language The language selection to set.
*/
public void setLanguage(String language)
{
this.language = language;
Application.setLanguage(FacesContext.getCurrentInstance(), this.language);
}
// ------------------------------------------------------------------------------
// Validator methods
/**
* Validate password field data is acceptable
*/
public void validatePassword(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
String pass = (String) value;
if (pass.length() < 3 || pass.length() > 32)
{
String err = MessageFormat.format(Application.getMessage(context, MSG_PASSWORD_LENGTH),
new Object[]{3, 32});
throw new ValidatorException(new FacesMessage(err));
}
}
/**
* Validate Username field data is acceptable
*/
public void validateUsername(FacesContext context, UIComponent component, Object value)
throws ValidatorException
{
String pass = (String) value;
if (pass.length() < 3 || pass.length() > 32)
{
String err = MessageFormat.format(Application.getMessage(context, MSG_USERNAME_LENGTH),
new Object[]{3, 32});
throw new ValidatorException(new FacesMessage(err));
}
}
// ------------------------------------------------------------------------------
// Action event methods
/**
* Login action handler
*
* @return outcome view name
*/
public String login()
{
String outcome = null;
FacesContext fc = FacesContext.getCurrentInstance();
if (this.username != null && this.password != null)
{
// Authenticate via the authentication service, then save the details of user in an object
// in the session - this is used by the servlet filter etc. on each page to check for login
try
{
this.authenticationService.authenticate(this.username, this.password.toCharArray());
// setup User object and Home space ID
User user = new User(this.authenticationService.getCurrentUserName(), this.authenticationService.getCurrentTicket(),
personService.getPerson(this.username));
NodeRef homeSpaceRef = (NodeRef) this.nodeService.getProperty(personService.getPerson(this.username), ContentModel.PROP_HOMEFOLDER);
// check that the home space node exists - else user cannot login
if (this.nodeService.exists(homeSpaceRef) == false)
{
throw new InvalidNodeRefException(homeSpaceRef);
}
user.setHomeSpaceId(homeSpaceRef.getId());
// put the User object in the Session - the authentication servlet will then allow
// the app to continue without redirecting to the login page
Map session = fc.getExternalContext().getSessionMap();
session.put(AuthenticationHelper.AUTHENTICATION_USER, user);
// if an external outcome has been provided then use that, else use default
String externalOutcome = (String)fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_KEY);
if (externalOutcome != null)
{
// TODO: This is a quick solution. It would be better to specify the (identifier?)
// of a handler class that would be responsible for processing specific outcome arguments.
if (logger.isDebugEnabled())
logger.debug("External outcome found: " + externalOutcome);
// setup is required for certain outcome requests
if (OUTCOME_DOCDETAILS.equals(externalOutcome))
{
NodeRef nodeRef = null;
String[] args = (String[]) fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_ARGS);
if (args[0].equals(WebDAVServlet.WEBDAV_PREFIX))
{
nodeRef = resolveWebDAVPath(fc, args);
}
else if (args.length == 3)
{
StoreRef storeRef = new StoreRef(args[0], args[1]);
nodeRef = new NodeRef(storeRef, args[2]);
}
if (nodeRef != null)
{
// setup the Document on the browse bean
// TODO: the browse bean should accept a full NodeRef - not just an ID
this.browseBean.setupContentAction(nodeRef.getId(), true);
}
}
else if (OUTCOME_SPACEDETAILS.equals(externalOutcome))
{
NodeRef nodeRef = null;
String[] args = (String[]) fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_ARGS);
if (args[0].equals(WebDAVServlet.WEBDAV_PREFIX))
{
nodeRef = resolveWebDAVPath(fc, args);
}
else if (args.length == 3)
{
StoreRef storeRef = new StoreRef(args[0], args[1]);
nodeRef = new NodeRef(storeRef, args[2]);
}
if (nodeRef != null)
{
// setup the Space on the browse bean
// TODO: the browse bean should accept a full NodeRef - not just an ID
this.browseBean.setupSpaceAction(nodeRef.getId(), true);
}
}
else if (OUTCOME_BROWSE.equals(externalOutcome))
{
String[] args = (String[]) fc.getExternalContext().getSessionMap().get(LOGIN_OUTCOME_ARGS);
if (args != null)
{
NodeRef nodeRef = null;
int offset = 0;
if (args.length >= 3)
{
offset = args.length - 3;
StoreRef storeRef = new StoreRef(args[0+offset], args[1+offset]);
nodeRef = new NodeRef(storeRef, args[2+offset]);
// setup the ref as current Id in the global navigation bean
this.navigator.setCurrentNodeId(nodeRef.getId());
// check for view mode first argument
if (args[0].equals(LOGIN_ARG_TEMPLATE))
{
this.browseBean.setDashboardView(true);
// the above call will auto-navigate to the correct outcome - so we don't!
externalOutcome = null;
}
}
}
}
fc.getExternalContext().getSessionMap().remove(LOGIN_OUTCOME_KEY);
return externalOutcome;
}
else
{
// if a redirect URL has been provided then use that
String redirectURL = (String)fc.getExternalContext().getSessionMap().get(LOGIN_REDIRECT_KEY);
if (redirectURL != null)
{
if (logger.isDebugEnabled())
logger.debug("Redirect URL found: " + redirectURL);
// remove URL from session
fc.getExternalContext().getSessionMap().remove(LOGIN_REDIRECT_KEY);
try
{
fc.getExternalContext().redirect(redirectURL);
fc.responseComplete();
return null;
}
catch (IOException ioErr)
{
logger.warn("Unable to redirect to url: " + redirectURL);
}
}
else
{
return "success";
}
}
}
catch (AuthenticationException aerr)
{
Utils.addErrorMessage(Application.getMessage(fc, MSG_ERROR_UNKNOWN_USER));
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(fc,
Repository.ERROR_NOHOME), refErr.getNodeRef().getId()));
}
}
else
{
Utils.addErrorMessage(Application.getMessage(fc, MSG_ERROR_MISSING));
}
return outcome;
}
/**
* Invalidate ticket and logout user
*/
public String logout()
{
FacesContext context = FacesContext.getCurrentInstance();
Map session = context.getExternalContext().getSessionMap();
User user = (User) session.get(AuthenticationHelper.AUTHENTICATION_USER);
boolean alfrescoAuth = (session.get(LOGIN_EXTERNAL_AUTH) == null);
// invalidate Session for this user
if (Application.inPortalServer() == false)
{
HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
request.getSession().invalidate();
}
else
{
PortletRequest request = (PortletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
request.getPortletSession().invalidate();
}
// invalidate User ticket
if (user != null)
{
this.authenticationService.invalidateTicket(user.getTicket());
}
// set language to last used
if (this.language != null && this.language.length() != 0)
{
Application.setLanguage(context, this.language);
}
return alfrescoAuth ? "logout" : "relogin";
}
// ------------------------------------------------------------------------------
// Private helpers
/**
* Resolves the given path elements to a NodeRef in the current repository
*
* @param context Faces context
* @param args The elements of the path to lookup
*/
private NodeRef resolveWebDAVPath(FacesContext context, String[] args)
{
NodeRef nodeRef = null;
List<String> paths = new ArrayList<String>(args.length-1);
FileInfo file = null;
try
{
// create a list of path elements (decode the URL as we go)
for (int x = 1; x < args.length; x++)
{
paths.add(URLDecoder.decode(args[x], "UTF-8"));
}
if (logger.isDebugEnabled())
logger.debug("Attempting to resolve webdav path to NodeRef: " + paths);
// get the company home node to start the search from
NodeRef companyHome = new NodeRef(Repository.getStoreRef(),
Application.getCompanyRootId());
file = this.fileFolderService.resolveNamePath(companyHome, paths);
nodeRef = file.getNodeRef();
}
catch (UnsupportedEncodingException uee)
{
if (logger.isWarnEnabled())
logger.warn("Failed to resolve webdav path", uee);
nodeRef = null;
}
catch (FileNotFoundException fne)
{
if (logger.isWarnEnabled())
logger.debug("Failed to resolve webdav path", fne);
nodeRef = null;
}
return nodeRef;
}
// ------------------------------------------------------------------------------
// Private data
private static final Log logger = LogFactory.getLog(LoginBean.class);
/** I18N messages */
private static final String MSG_ERROR_MISSING = "error_login_missing";
private static final String MSG_ERROR_UNKNOWN_USER = "error_login_user";
private static final String MSG_USERNAME_CHARS = "login_err_username_chars";
private static final String MSG_USERNAME_LENGTH = "login_err_username_length";
private static final String MSG_PASSWORD_CHARS = "login_err_password_chars";
private static final String MSG_PASSWORD_LENGTH = "login_err_password_length";
public static final String LOGIN_REDIRECT_KEY = "_alfRedirect";
public static final String LOGIN_OUTCOME_KEY = "_alfOutcome";
public static final String LOGIN_OUTCOME_ARGS = "_alfOutcomeArgs";
public static final String LOGIN_EXTERNAL_AUTH= "_alfExternalAuth";
public final static String OUTCOME_DOCDETAILS = "showDocDetails";
public final static String OUTCOME_SPACEDETAILS = "showSpaceDetails";
public final static String OUTCOME_BROWSE = "browse";
private static final String LOGIN_ARG_TEMPLATE = "template";
/** user name */
private String username = null;
/** password */
private String password = null;
/** language locale selection */
private String language = null;
/** PersonService bean reference */
private PersonService personService;
/** AuthenticationService bean reference */
private AuthenticationService authenticationService;
/** NodeService bean reference */
private NodeService nodeService;
/** The BrowseBean reference */
private BrowseBean browseBean;
/** The NavigationBean bean reference */
private NavigationBean navigator;
/** ConfigService bean reference */
private ConfigService configService;
/** FileFolderService bean reference */
private FileFolderService fileFolderService;
}

View File

@@ -0,0 +1,674 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.alfresco.config.ConfigService;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.CIFSServer;
import org.alfresco.filesys.server.filesys.DiskSharedDevice;
import org.alfresco.filesys.smb.server.repo.ContentContext;
import org.alfresco.filesys.smb.server.repo.ContentDiskInterface;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.AlfrescoNavigationHandler;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.bean.wizard.NewSpaceWizard;
import org.alfresco.web.config.ClientConfigElement;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.IBreadcrumbHandler;
import org.alfresco.web.ui.common.component.UIBreadcrumb;
import org.alfresco.web.ui.common.component.UIModeList;
import org.alfresco.web.ui.repo.component.IRepoBreadcrumbHandler;
import org.alfresco.web.ui.repo.component.shelf.UIShelf;
import org.apache.log4j.Logger;
/**
* @author Kevin Roast
*/
public class NavigationBean
{
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param nodeService The nodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param searchService The searchService to set.
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param namespaceService The namespaceService to set.
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param cifsServer The cifsServer to set.
*/
public void setCifsServer(CIFSServer cifsServer)
{
this.cifsServer = cifsServer;
}
/**
* @param contentDiskDriver The contentDiskDriver to set.
*/
public void setContentDiskDriver(ContentDiskInterface contentDiskDriver)
{
this.contentDiskDriver = contentDiskDriver;
}
/**
* @param configService The ConfigService to set.
*/
public void setConfigService(ConfigService configService)
{
this.configService = configService;
}
/**
* @return the User object representing the current instance for this user
*/
public User getCurrentUser()
{
return Application.getCurrentUser(FacesContext.getCurrentInstance());
}
/**
* Return the expanded state of the Shelf panel wrapper component
*
* @return the expanded state of the Shelf panel wrapper component
*/
public boolean isShelfExpanded()
{
return this.shelfExpanded;
}
/**
* Set the expanded state of the Shelf panel wrapper component
*
* @param expanded true to expanded the Shelf panel area, false to hide it
*/
public void setShelfExpanded(boolean expanded)
{
this.shelfExpanded = expanded;
}
/**
* @return Returns the array containing the expanded state of the shelf items
*/
public boolean[] getShelfItemExpanded()
{
return this.shelfItemExpanded;
}
/**
* @param shelfItemExpanded The array containing the expanded state of the shelf items
*/
public void setShelfItemExpanded(boolean[] shelfItemExpanded)
{
this.shelfItemExpanded = shelfItemExpanded;
}
/**
* @return Returns the toolbar Location.
*/
public String getToolbarLocation()
{
return this.toolbarLocation;
}
/**
* @param toolbarLocation The toolbar Location to set.
*/
public void setToolbarLocation(String toolbarLocation)
{
this.toolbarLocation = toolbarLocation;
}
/**
* @return Returns the helpUrl.
*/
public String getHelpUrl()
{
if (this.clientConfig == null)
{
initFromClientConfig();
}
return this.helpUrl;
}
/**
* @param helpUrl The helpUrl to set.
*/
public void setHelpUrl(String helpUrl)
{
this.helpUrl = helpUrl;
}
/**
* @return Returns the search context object if any.
*/
public SearchContext getSearchContext()
{
return this.searchContext;
}
/**
* @param searchContext The search context object to set or null to clear search.
*/
public void setSearchContext(SearchContext searchContext)
{
this.searchContext = searchContext;
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
/**
* @return Returns the currently browsing node Id.
*/
public String getCurrentNodeId()
{
return this.currentNodeId;
}
/**
* Set the node Id of the current folder/space container node.
* <p>
* Setting this value causes the UI to update and display the specified node as current.
*
* @param currentNodeId The currently browsing node Id.
*/
public void setCurrentNodeId(String currentNodeId)
{
if (s_logger.isDebugEnabled())
s_logger.debug("Setting current node id to: " + currentNodeId);
if (currentNodeId == null)
{
throw new AlfrescoRuntimeException("Can not set the current node id to null");
}
// set the current Node Id for our UI context operations
this.currentNodeId = currentNodeId;
// clear other context that is based on or relevant to the Node id
this.currentNode = null;
this.searchContext = null;
// inform any interested beans that the UI needs updating after this change
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
// clear current node context after the notify - this is to ensure that if any delegates
// performed operations on the current node, that we have fresh data for the next View
this.currentNode = null;
}
/**
* @return true if the current node has a template view available
*/
public boolean getCurrentNodeHasTemplate()
{
boolean templateView = false;
Node node = getCurrentNode();
if (node.hasAspect(ContentModel.ASPECT_TEMPLATABLE))
{
NodeRef templateRef = (NodeRef)node.getProperties().get(ContentModel.PROP_TEMPLATE);
templateView = (templateRef != null && this.nodeService.exists(templateRef));
}
return templateView;
}
/**
* @return the NodeRef.toString() for the current node template view if it has one set
*/
public String getCurrentNodeTemplate()
{
String strRef = null;
if (getCurrentNodeHasTemplate() == true)
{
strRef = getCurrentNode().getProperties().get(ContentModel.PROP_TEMPLATE).toString();
}
return strRef;
}
/**
* Returns a model for use by a template on a space Dashboard page.
*
* @return model containing current current space info.
*/
public Map getTemplateModel()
{
HashMap model = new HashMap(1, 1.0f);
FacesContext fc = FacesContext.getCurrentInstance();
TemplateNode spaceNode = new TemplateNode(getCurrentNode().getNodeRef(), Repository.getServiceRegistry(fc),
new TemplateImageResolver() {
public String resolveImagePathForName(String filename, boolean small) {
return Utils.getFileTypeImage(filename, small);
}
});
model.put("space", spaceNode);
return model;
}
/**
* Clear state so that the current node properties cache for the next time they are requested
*/
public void resetCurrentNodeProperties()
{
this.currentNode = null;
}
/**
* @return The Map of properties for the current Node.
*/
public Map<String, Object> getNodeProperties()
{
return getCurrentNode().getProperties();
}
/**
* @return The current Node object for UI context operations
*/
public Node getCurrentNode()
{
if (this.currentNode == null)
{
if (this.currentNodeId == null)
{
throw new AlfrescoRuntimeException("Cannot retrieve current Node if NodeId is null!");
}
if (s_logger.isDebugEnabled())
s_logger.debug("Caching properties for node id: " + this.currentNodeId);
NodeRef nodeRef;
Node node;
Map<String, Object> props;
try
{
// build a node which components on the JSP page can bind too
nodeRef = new NodeRef(Repository.getStoreRef(), this.currentNodeId);
node = new Node(nodeRef);
// early init properties for this node (by getProperties() call)
// resolve icon in-case one has not been set
props = node.getProperties();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), ERROR_DELETED_FOLDER), new Object[] {this.currentNodeId}) );
nodeRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
node = new Node(nodeRef);
props = node.getProperties();
}
String icon = (String)props.get("app:icon");
props.put("icon", icon != null ? icon : NewSpaceWizard.SPACE_ICON_DEFAULT);
Path path = this.nodeService.getPath(nodeRef);
// resolve CIFS network folder location for this node
DiskSharedDevice diskShare = cifsServer.getConfiguration().getPrimaryFilesystem();
if (diskShare != null)
{
ContentContext contentCtx = (ContentContext) diskShare.getContext();
NodeRef rootNode = contentCtx.getRootNode();
String cifsPath = Repository.getNamePath(this.nodeService, path, rootNode, "\\", "file:///" + getCIFSServerPath(diskShare));
node.getProperties().put("cifsPath", cifsPath);
node.getProperties().put("cifsPathLabel", cifsPath.substring(8)); // strip file:/// part
}
this.currentNode = node;
}
return this.currentNode;
}
/**
* @return Returns the breadcrumb handler elements representing the location path of the UI.
*/
public List<IBreadcrumbHandler> getLocation()
{
if (this.location == null)
{
// init the location from the User object for the first time
User user = Application.getCurrentUser(FacesContext.getCurrentInstance());
NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId());
String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef);
// set the current node to the users Home Space Id
setCurrentNodeId(user.getHomeSpaceId());
// setup the breadcrumb with the same location
List<IBreadcrumbHandler> elements = new ArrayList<IBreadcrumbHandler>(1);
elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName));
setLocation(elements);
}
return this.location;
}
/**
* @param location The UI location representation to set.
*/
public void setLocation(List<IBreadcrumbHandler> location)
{
this.location = location;
}
/**
* Sets up the dispatch context so that the navigation handler knows
* what object is being acted upon
*
* @param node The node to be added to the dispatch context
*/
public void setupDispatchContext(Node node)
{
this.dispatchContext = node;
}
/**
* Resets the dispatch context
*/
public void resetDispatchContext()
{
this.dispatchContext = null;
}
/**
* Returns the node currently set in the dispatch context
*
* @return The node being dispatched or null if there is no
* dispatch context
*/
public Node getDispatchContextNode()
{
return this.dispatchContext;
}
// ------------------------------------------------------------------------------
// Navigation action event handlers
/**
* Action handler to toggle the expanded state of the shelf.
* The panel component wrapping the shelf area of the UI is value bound to the shelfExpanded property.
*/
public void toggleShelf(ActionEvent event)
{
this.shelfExpanded = !this.shelfExpanded;
}
/**
* Action handler called after a Shelf Group has had its expanded state toggled by the user
*/
public void shelfGroupToggled(ActionEvent event)
{
UIShelf.ShelfEvent shelfEvent = (UIShelf.ShelfEvent)event;
this.shelfItemExpanded[shelfEvent.Index] = shelfEvent.Expanded;
}
/**
* Action to change the toolbar location
* Currently this will changed the location from Company to the users Home space
*/
public void toolbarLocationChanged(ActionEvent event)
{
FacesContext context = FacesContext.getCurrentInstance();
try
{
UIModeList locationList = (UIModeList)event.getComponent();
String location = locationList.getValue().toString();
setToolbarLocation(location);
if (LOCATION_COMPANY.equals(location))
{
List<IBreadcrumbHandler> elements = new ArrayList<IBreadcrumbHandler>(1);
NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
String companySpaceName = Repository.getNameForNode(this.nodeService, companyRootRef);
elements.add(new NavigationBreadcrumbHandler(companyRootRef, companySpaceName));
setLocation(elements);
setCurrentNodeId(companyRootRef.getId());
}
else if (LOCATION_HOME.equals(location))
{
List<IBreadcrumbHandler> elements = new ArrayList<IBreadcrumbHandler>(1);
String homeSpaceId = Application.getCurrentUser(context).getHomeSpaceId();
NodeRef homeSpaceRef = new NodeRef(Repository.getStoreRef(), homeSpaceId);
String homeSpaceName = Repository.getNameForNode(this.nodeService, homeSpaceRef);
elements.add(new NavigationBreadcrumbHandler(homeSpaceRef, homeSpaceName));
setLocation(elements);
setCurrentNodeId(homeSpaceRef.getId());
}
// we need to force a navigation to refresh the browse screen breadcrumb
context.getApplication().getNavigationHandler().handleNavigation(context, null, "browse");
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NOHOME), Application.getCurrentUser(context).getHomeSpaceId()), refErr );
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
}
}
/**
* @param diskShare Filesystem shared device
* @return CIFS server path as network style string label
*/
public String getCIFSServerPath(DiskSharedDevice diskShare)
{
if (this.cifsServerPath == null)
{
StringBuilder buf = new StringBuilder(24);
String serverName = this.cifsServer.getConfiguration().getServerName();
if (serverName != null && serverName.length() != 0)
{
buf.append("\\\\")
.append(serverName)
.append("\\");
buf.append(diskShare.getName());
}
this.cifsServerPath = buf.toString();
}
return this.cifsServerPath;
}
// ------------------------------------------------------------------------------
// Private helpers
/**
* Initialise default values from client configuration
*/
private void initFromClientConfig()
{
this.clientConfig = (ClientConfigElement)this.configService.getGlobalConfig().getConfigElement(
ClientConfigElement.CONFIG_ELEMENT_ID);
this.helpUrl = clientConfig.getHelpUrl();
}
// ------------------------------------------------------------------------------
// Inner classes
/**
* Class to handle breadcrumb interaction for top-level navigation pages
*/
public class NavigationBreadcrumbHandler implements IRepoBreadcrumbHandler
{
private static final long serialVersionUID = 4833194653193016638L;
/**
* Constructor
*
* @param label Element label
*/
public NavigationBreadcrumbHandler(NodeRef ref, String label)
{
this.label = label;
this.ref = ref;
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
return this.label;
}
/**
* @see org.alfresco.web.ui.common.component.IBreadcrumbHandler#navigationOutcome(org.alfresco.web.ui.common.component.UIBreadcrumb)
*/
public String navigationOutcome(UIBreadcrumb breadcrumb)
{
// set the current node to the specified top level node ID
FacesContext fc = FacesContext.getCurrentInstance();
setCurrentNodeId(ref.getId());
setLocation( (List)breadcrumb.getValue() );
// setup the dispatch context
setupDispatchContext(new Node(ref));
if (fc.getViewRoot().getViewId().equals(BrowseBean.BROWSE_VIEW_ID))
{
return null;
}
else
{
return "browse";
}
}
public NodeRef getNodeRef()
{
return this.ref;
}
private String label;
private NodeRef ref;
}
// ------------------------------------------------------------------------------
// Private data
private static Logger s_logger = Logger.getLogger(NavigationBean.class);
/** constant values used by the toolbar location modelist control */
private static final String LOCATION_COMPANY = "company";
private static final String LOCATION_HOME = "home";
private static final String ERROR_DELETED_FOLDER = "error_deleted_folder";
/** The NodeService to be used by the bean */
private NodeService nodeService;
/** The SearchService to be used by the bean */
private SearchService searchService;
/** NamespaceService bean reference */
private NamespaceService namespaceService;
/** CIFSServer bean reference */
private CIFSServer cifsServer;
/** CIFS content disk driver bean reference */
private ContentDiskInterface contentDiskDriver;
/** ConfigService bean reference */
private ConfigService configService;
/** Client configuration object */
private ClientConfigElement clientConfig = null;
/** Cached path to our CIFS server and top level node DIR */
private String cifsServerPath;
/** Node Id we are using for UI context operations */
private String currentNodeId;
/** Node we are using for UI context operations */
private Node currentNode = null;
/** Node we are using for dispatching */
private Node dispatchContext = null;
/** Current toolbar location */
private String toolbarLocation = LOCATION_HOME;
/** Search context object we are currently using or null for no search */
private SearchContext searchContext;
/** expanded state of the Shelf panel wrapper component */
private boolean shelfExpanded = true;
/** expanded state of the Shelf item components */
private boolean[] shelfItemExpanded = new boolean[] {true, true, true, false, false};
/** list of the breadcrumb handler elements representing the location path of the UI */
private List<IBreadcrumbHandler> location = null;
/** The client Help file url */
private String helpUrl;
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.alfresco.config.ConfigService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.config.ClientConfigElement;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.repo.component.shelf.UIRecentSpacesShelfItem;
import org.apache.log4j.Logger;
/**
* This bean manages the real-time updated list of Recent Spaces in the Shelf component.
* <p>
* Registers itself as a UI Context Listener so it is informed as to when the current Node ID
* has changed in the NavigationBeans. This is used to keep the list of spaces up-to-date.
*
* @author Kevin Roast
*/
public class RecentSpacesBean implements IContextListener
{
private static Logger logger = Logger.getLogger(RecentSpacesBean.class);
/** The NavigationBean reference */
private NavigationBean navigator;
/** The BrowseBean reference */
private BrowseBean browseBean;
/** ConfigService bean reference */
private ConfigService configService;
/** Maximum number of recent spaces to show */
private Integer maxRecentSpaces = null;
/** List of recent space nodes */
private List<Node> recentSpaces = new LinkedList<Node>();
// ------------------------------------------------------------------------------
// Construction
/**
* Default Constructor
*/
public RecentSpacesBean()
{
UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
}
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @param configService The ConfigService to set.
*/
public void setConfigService(ConfigService configService)
{
this.configService = configService;
}
/**
* @return the List of recent spaces
*/
public List<Node> getRecentSpaces()
{
return this.recentSpaces;
}
/**
* @param spaces List of Nodes
*/
public void setRecentSpaces(List<Node> spaces)
{
this.recentSpaces = spaces;
}
// ------------------------------------------------------------------------------
// Action method handlers
/**
* Action handler bound to the recent spaces Shelf component called when a Space is clicked
*/
public void navigate(ActionEvent event)
{
// work out which node was clicked from the event data
UIRecentSpacesShelfItem.RecentSpacesEvent spaceEvent = (UIRecentSpacesShelfItem.RecentSpacesEvent)event;
Node selectedNode = this.recentSpaces.get(spaceEvent.Index);
NodeRef nodeRef = selectedNode.getNodeRef();
try
{
// then navigate to the appropriate node in UI
// use browse bean functionality for this as it will update the breadcrumb for us
this.browseBean.updateUILocation(nodeRef);
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {nodeRef.getId()}) );
// remove invalid node from recent spaces list
this.recentSpaces.remove(spaceEvent.Index);
}
}
// ------------------------------------------------------------------------------
// IContextListener implementation
/**
* @see org.alfresco.web.app.context.IContextListener#contextUpdated()
*/
public void contextUpdated()
{
// We use this listener handler to refresh the recent spaces list. At the point
// where this method is called, the current node Id in UI will probably have changed.
Node node = this.navigator.getCurrentNode();
// search for this node - if it's already in the list remove it so
// that it appears at the top for us
for (int i=0; i<this.recentSpaces.size(); i++)
{
if (node.getId().equals(this.recentSpaces.get(i).getId()))
{
// found same node already in the list - remove it
this.recentSpaces.remove(i);
break;
}
}
// remove an item if the list is at the maximum length
int maxItems = getMaxRecentSpaces();
if (this.recentSpaces.size() == maxItems)
{
this.recentSpaces.remove(maxItems - 1);
}
if (logger.isDebugEnabled())
logger.debug("Inserting node: " + node.getName() + " at top of recent spaces list.");
// insert our Node at the top of the list so it's most relevent
this.recentSpaces.add(0, node);
}
/**
* @return the max number of recent spaces to show, retrieved from client config
*/
private int getMaxRecentSpaces()
{
if (maxRecentSpaces == null)
{
ClientConfigElement config = (ClientConfigElement)this.configService.getGlobalConfig().getConfigElement(
ClientConfigElement.CONFIG_ELEMENT_ID);
maxRecentSpaces = Integer.valueOf(config.getRecentSpacesItems());
}
return maxRecentSpaces.intValue();
}
}

View File

@@ -0,0 +1,327 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.UIModeList;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Backing bean for the manage content rules dialog
*
* @author gavinc
*/
public class RulesBean
{
private static final String MSG_ERROR_DELETE_RULE = "error_delete_rule";
private static final String LOCAL = "local";
private static final String INHERITED = "inherited";
private static Log logger = LogFactory.getLog(RulesBean.class);
private String viewMode = INHERITED;
private BrowseBean browseBean;
private RuleService ruleService;
private List<WrappedRule> rules;
private Rule currentRule;
private UIRichList richList;
/**
* Returns the current view mode the list of rules is in
*
* @return The current view mode
*/
public String getViewMode()
{
return this.viewMode;
}
/**
* @return The space to work against
*/
public Node getSpace()
{
return this.browseBean.getActionSpace();
}
/**
* Returns the list of rules to display
*
* @return
*/
public List<WrappedRule> getRules()
{
boolean includeInherited = true;
if (this.viewMode.equals(LOCAL))
{
includeInherited = false;
}
// get the rules from the repository
List<Rule> repoRules = this.ruleService.getRules(getSpace().getNodeRef(), includeInherited);
this.rules = new ArrayList<WrappedRule>(repoRules.size());
// wrap them all passing the current space
for (Rule rule : repoRules)
{
WrappedRule wrapped = new WrappedRule(rule, getSpace().getNodeRef());
this.rules.add(wrapped);
}
return this.rules;
}
/**
* Handles a rule being clicked ready for an action i.e. edit or delete
*
* @param event The event representing the click
*/
public void setupRuleAction(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
if (logger.isDebugEnabled())
logger.debug("Rule clicked, it's id is: " + id);
this.currentRule = this.ruleService.getRule(
getSpace().getNodeRef(), id);
}
}
/**
* Returns the current rule
*
* @return The current rule
*/
public Rule getCurrentRule()
{
return this.currentRule;
}
/**
* Handler called upon the completion of the Delete Rule page
*
* @return outcome
*/
public String deleteOK()
{
String outcome = null;
if (this.currentRule != null)
{
try
{
String ruleTitle = this.currentRule.getTitle();
this.ruleService.removeRule(getSpace().getNodeRef(),
this.currentRule);
// clear the current rule
this.currentRule = null;
// setting the outcome will show the browse view again
outcome = "manageRules";
if (logger.isDebugEnabled())
logger.debug("Deleted rule '" + ruleTitle + "'");
}
catch (Throwable err)
{
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_DELETE_RULE) + err.getMessage(), err);
}
}
else
{
logger.warn("WARNING: deleteOK called without a current Rule!");
}
return outcome;
}
/**
* Change the current view mode based on user selection
*
* @param event ActionEvent
*/
public void viewModeChanged(ActionEvent event)
{
UIModeList viewList = (UIModeList)event.getComponent();
this.viewMode = viewList.getValue().toString();
// force the list to be re-queried when the page is refreshed
if (this.richList != null)
{
this.richList.setValue(null);
}
}
/**
* Sets the UIRichList component being used by this backing bean
*
* @param richList UIRichList component
*/
public void setRichList(UIRichList richList)
{
this.richList = richList;
this.richList.setValue(null);
}
/**
* Returns the UIRichList component being used by this backing bean
*
* @return UIRichList component
*/
public UIRichList getRichList()
{
return this.richList;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @param ruleService Sets the rule service to use
*/
public void setRuleService(RuleService ruleService)
{
this.ruleService = ruleService;
}
/**
* Inner class to wrap the Rule objects so we can expose a flag to indicate whether
* the rule is a local or inherited rule
*/
public class WrappedRule
{
private Rule rule;
private NodeRef ruleNode;
/**
* Constructs a RuleWrapper object
*
* @param rule The rule we are wrapping
* @param ruleNode The node the rules belong to
*/
public WrappedRule(Rule rule, NodeRef ruleNode)
{
this.rule = rule;
this.ruleNode = ruleNode;
}
/**
* Returns the rule being wrapped
*
* @return The wrapped Rule
*/
public Rule getRule()
{
return this.rule;
}
/**
* Determines whether the current rule is a local rule or
* has been inherited from a parent
*
* @return true if the rule is defined on the current node
*/
public boolean getLocal()
{
return ruleNode.equals(this.rule.getOwningNodeRef());
}
/** Methods to support sorting of the rules list in a table */
/**
* Returns the rule id
*
* @return The id
*/
public String getId()
{
return this.rule.getId();
}
/**
* Returns the rule title
*
* @return The title
*/
public String getTitle()
{
return this.rule.getTitle();
}
/**
* Returns the rule description
*
* @return The description
*/
public String getDescription()
{
return this.rule.getDescription();
}
/**
* Returns the created date
*
* @return The created date
*/
public Date getCreatedDate()
{
return this.rule.getCreatedDate();
}
/**
* Returns the modfified date
*
* @return The modified date
*/
public Date getModifiedDate()
{
return this.rule.getModifiedDate();
}
}
}

View File

@@ -0,0 +1,542 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.faces.context.FacesContext;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.ISO9075;
import org.alfresco.repo.search.impl.lucene.QueryParser;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.bean.repository.Repository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Holds the context required to build a search query and can return the populated query.
*
* @author Kevin Roast
*/
public final class SearchContext implements Serializable
{
/** Search mode constants */
public final static int SEARCH_ALL = 0;
public final static int SEARCH_FILE_NAMES_CONTENTS = 1;
public final static int SEARCH_FILE_NAMES = 2;
public final static int SEARCH_SPACE_NAMES = 3;
/** the search text string */
private String text = "";
/** mode for the search */
private int mode = SearchContext.SEARCH_ALL;
/** folder node location for the search */
private String location = null;
/** categories to add to the search */
private String[] categories = new String[0];
/** true to search location children as well as location */
private boolean locationChildren = true;
/** true to search category children as well as category */
private boolean categoryChildren = true;
/** content type to restrict search against */
private String contentType = null;
/** content mimetype to restrict search against */
private String mimeType = null;
/** any extra query attributes to add to the search */
private Map<QName, String> queryAttributes = new HashMap<QName, String>(5, 1.0f);
/** any additional range attribute to add to the search */
private Map<QName, RangeProperties> rangeAttributes = new HashMap<QName, RangeProperties>(5, 1.0f);
/** any additional fixed value attributes to add to the search, such as boolean or noderef */
private Map<QName, String> queryFixedValues = new HashMap<QName, String>(5, 1.0f);
/** logger */
private static Log logger = LogFactory.getLog(SearchContext.class);
/**
* Build the search query string based on the current search context members.
*
* @return prepared search query string
*/
public String buildQuery()
{
String query;
// the QName for the well known "name" attribute
String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"));
// match against content text
String text = this.text.trim();
String fullTextQuery;
String nameAttrQuery;
if (text.indexOf(' ') == -1)
{
// simple single word text search
if (text.charAt(0) != '*')
{
// escape characters and append the wildcard character
String safeText = QueryParser.escape(text);
fullTextQuery = " TEXT:" + safeText + '*';
nameAttrQuery = " @" + nameAttr + ":" + safeText + '*';
}
else
{
// found a leading wildcard - prepend it again after escaping the other characters
String safeText = QueryParser.escape(text.substring(1));
fullTextQuery = " TEXT:*" + safeText + '*';
nameAttrQuery = " @" + nameAttr + ":*" + safeText + '*';
}
}
else
{
// multiple word search
if (text.charAt(0) == '"' && text.charAt(text.length() - 1) == '"')
{
// as quoted phrase
String quotedSafeText = '"' + QueryParser.escape(text.substring(1, text.length() - 1)) + '"';
fullTextQuery = " TEXT:" + quotedSafeText;
nameAttrQuery = " @" + nameAttr + ":" + quotedSafeText;
}
else
{
// as individual search terms
StringTokenizer t = new StringTokenizer(text, " ");
StringBuilder fullTextBuf = new StringBuilder(64);
StringBuilder nameAttrBuf = new StringBuilder(64);
fullTextBuf.append('(');
nameAttrBuf.append('(');
while (t.hasMoreTokens())
{
String term = t.nextToken();
if (term.charAt(0) != '*')
{
String safeTerm = QueryParser.escape(term);
fullTextBuf.append("TEXT:").append(safeTerm).append('*');
nameAttrBuf.append("@").append(nameAttr).append(":").append(safeTerm).append('*');
}
else
{
String safeTerm = QueryParser.escape(term.substring(1));
fullTextBuf.append("TEXT:*").append(safeTerm).append('*');
nameAttrBuf.append("@").append(nameAttr).append(":*").append(safeTerm).append('*');
}
if (t.hasMoreTokens())
{
fullTextBuf.append(" OR ");
nameAttrBuf.append(" OR ");
}
}
fullTextBuf.append(')');
nameAttrBuf.append(')');
fullTextQuery = fullTextBuf.toString();
nameAttrQuery = nameAttrBuf.toString();
}
}
// match a specific PATH for space location or categories
StringBuilder pathQuery = null;
if (location != null || (categories != null && categories.length !=0))
{
pathQuery = new StringBuilder(128);
if (location != null)
{
pathQuery.append(" PATH:\"").append(location).append("\" ");
}
if (categories != null && categories.length != 0)
{
for (int i=0; i<categories.length; i++)
{
if (pathQuery.length() != 0)
{
pathQuery.append("OR");
}
pathQuery.append(" PATH:\"").append(categories[i]).append("\" ");
}
}
}
// match any extra query attribute values specified
StringBuilder attributeQuery = null;
if (queryAttributes.size() != 0)
{
attributeQuery = new StringBuilder(queryAttributes.size() << 6);
for (QName qname : queryAttributes.keySet())
{
String escapedName = Repository.escapeQName(qname);
String value = QueryParser.escape(queryAttributes.get(qname));
attributeQuery.append(" +@").append(escapedName)
.append(":").append(value).append('*');
}
}
// match any extra fixed value attributes specified
if (queryFixedValues.size() != 0)
{
if (attributeQuery == null)
{
attributeQuery = new StringBuilder(queryFixedValues.size() << 6);
}
for (QName qname : queryFixedValues.keySet())
{
String escapedName = Repository.escapeQName(qname);
String value = queryFixedValues.get(qname);
attributeQuery.append(" +@").append(escapedName)
.append(":\"").append(value).append('"');
}
}
// range attributes are a special case also
if (rangeAttributes.size() != 0)
{
if (attributeQuery == null)
{
attributeQuery = new StringBuilder(rangeAttributes.size() << 6);
}
for (QName qname : rangeAttributes.keySet())
{
String escapedName = Repository.escapeQName(qname);
RangeProperties rp = rangeAttributes.get(qname);
String value1 = QueryParser.escape(rp.lower);
String value2 = QueryParser.escape(rp.upper);
attributeQuery.append(" +@").append(escapedName)
.append(":").append(rp.inclusive ? "[" : "{").append(value1)
.append(" TO ").append(value2).append(rp.inclusive ? "]" : "}");
}
}
// mimetype is a special case - it is indexed as a special attribute it comes from the combined
// ContentData attribute of cm:content - ContentData string cannot be searched directly
if (mimeType != null && mimeType.length() != 0)
{
if (attributeQuery == null)
{
attributeQuery = new StringBuilder(64);
}
String escapedName = Repository.escapeQName(QName.createQName(ContentModel.PROP_CONTENT + ".mimetype"));
attributeQuery.append(" +@").append(escapedName)
.append(":").append(mimeType);
}
// match against appropriate content type
String fileTypeQuery;
if (contentType != null)
{
fileTypeQuery = " +TYPE:\"" + contentType + "\" ";
}
else
{
// default to cm:content
fileTypeQuery = " +TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}content\" ";
}
// match against FOLDER type
String folderTypeQuery = " +TYPE:\"{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}folder\" ";
switch (mode)
{
case SearchContext.SEARCH_ALL:
query = '(' + fileTypeQuery + " AND " + '(' + nameAttrQuery + fullTextQuery + ')' + ')' + " OR " +
'(' + folderTypeQuery + " AND " + nameAttrQuery + ')';
break;
case SearchContext.SEARCH_FILE_NAMES:
query = fileTypeQuery + " AND " + nameAttrQuery;
break;
case SearchContext.SEARCH_FILE_NAMES_CONTENTS:
query = fileTypeQuery + " AND " + '(' + nameAttrQuery + fullTextQuery + ')';
break;
case SearchContext.SEARCH_SPACE_NAMES:
query = folderTypeQuery + " AND " + nameAttrQuery;
break;
default:
throw new IllegalStateException("Unknown search mode specified: " + mode);
}
// match entire query against any additional attributes specified
if (attributeQuery != null)
{
query = attributeQuery + " AND (" + query + ')';
}
// match entire query against specified Space path
if (pathQuery != null)
{
query = pathQuery + " AND (" + query + ')';
}
if (logger.isDebugEnabled())
logger.debug("Query: " + query);
return query;
}
/**
* Generate a search XPATH pointing to the specified node Id, optionally return an XPATH
* that includes the child nodes.
*
* @param id Of the node to generate path too
* @param children Whether to include children of the node
*
* @return the path
*/
/*package*/ static String getPathFromSpaceRef(NodeRef ref, boolean children)
{
FacesContext context = FacesContext.getCurrentInstance();
Path path = Repository.getServiceRegistry(context).getNodeService().getPath(ref);
NamespaceService ns = Repository.getServiceRegistry(context).getNamespaceService();
StringBuilder buf = new StringBuilder(64);
for (int i=0; i<path.size(); i++)
{
String elementString = "";
Path.Element element = path.get(i);
if (element instanceof Path.ChildAssocElement)
{
ChildAssociationRef elementRef = ((Path.ChildAssocElement)element).getRef();
if (elementRef.getParentRef() != null)
{
Collection prefixes = ns.getPrefixes(elementRef.getQName().getNamespaceURI());
if (prefixes.size() >0)
{
elementString = '/' + (String)prefixes.iterator().next() + ':' + ISO9075.encode(elementRef.getQName().getLocalName());
}
}
}
buf.append(elementString);
}
if (children == true)
{
// append syntax to get all children of the path
buf.append("//*");
}
else
{
// append syntax to just represent the path, not the children
buf.append("/*");
}
return buf.toString();
}
/**
* @return Returns the categories to use for the search
*/
public String[] getCategories()
{
return this.categories;
}
/**
* @param categories The categories to set.
*/
public void setCategories(String[] categories)
{
if (categories != null)
{
this.categories = categories;
}
}
/**
* @return Returns the node to search from or null for all.
*/
public String getLocation()
{
return this.location;
}
/**
* @param location The node to search from or null for all..
*/
public void setLocation(String location)
{
this.location = location;
}
/**
* @return Returns the mode to use during the search (see constants)
*/
public int getMode()
{
return this.mode;
}
/**
* @param mode The mode to use during the search (see constants)
*/
public void setMode(int mode)
{
this.mode = mode;
}
/**
* @return Returns the search text string.
*/
public String getText()
{
return this.text;
}
/**
* @param text The search text string.
*/
public void setText(String text)
{
this.text = text;
}
/**
* @return Returns the contentType.
*/
public String getContentType()
{
return this.contentType;
}
/**
* @param contentType The content type to restrict attribute search against.
*/
public void setContentType(String contentType)
{
this.contentType = contentType;
}
/**
* @return Returns the mimeType.
*/
public String getMimeType()
{
return this.mimeType;
}
/**
* @param mimeType The mimeType to set.
*/
public void setMimeType(String mimeType)
{
this.mimeType = mimeType;
}
/**
* @return Returns true to search location children, false for just the specified location.
*/
public boolean getLocationChildren()
{
return this.locationChildren;
}
/**
* @param locationChildren True to search location children, false for just the specified location.
*/
public void setLocationChildren(boolean locationChildren)
{
this.locationChildren = locationChildren;
}
/**
* @return Returns true to search category children, false for just the specified category.
*/
public boolean getCategoryChildren()
{
return this.categoryChildren;
}
/**
* @param categoryChildren True to search category children, false for just the specified category.
*/
public void setCategoryChildren(boolean categoryChildren)
{
this.categoryChildren = categoryChildren;
}
/**
* Add an additional attribute to search against
*
* @param qname QName of the attribute to search against
* @param value Value of the attribute to use
*/
public void addAttributeQuery(QName qname, String value)
{
this.queryAttributes.put(qname, value);
}
/**
* Add an additional range attribute to search against
*
* @param qname QName of the attribute to search against
* @param lower Lower value for range
* @param upper Upper value for range
* @param inclusive True for inclusive within the range, false otherwise
*/
public void addRangeQuery(QName qname, String lower, String upper, boolean inclusive)
{
this.rangeAttributes.put(qname, new RangeProperties(qname, lower, upper, inclusive));
}
/**
* Add an additional fixed value attribute to search against
*
* @param qname QName of the attribute to search against
* @param value Fixed value of the attribute to use
*/
public void addFixedValueQuery(QName qname, String value)
{
this.queryFixedValues.put(qname, value);
}
/**
* Simple wrapper class for range query attribute properties
*/
private static class RangeProperties
{
QName qname;
String lower;
String upper;
boolean inclusive;
RangeProperties(QName qname, String lower, String upper, boolean inclusive)
{
this.qname = qname;
this.lower = lower;
this.upper = upper;
this.inclusive = inclusive;
}
}
}

View File

@@ -0,0 +1,407 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.Utils.URLMode;
import org.alfresco.web.ui.common.component.UIActionLink;
/**
* Back bean provided access to the details of a Space
*
* @author Kevin Roast
*/
public class SpaceDetailsBean
{
private static final String OUTCOME_RETURN = "showSpaceDetails";
/** BrowseBean instance */
private BrowseBean browseBean;
/** The NavigationBean bean reference */
private NavigationBean navigator;
/** PermissionService bean reference */
private PermissionService permissionService;
/** NodeServuce bean reference */
private NodeService nodeService;
/** Selected template Id */
private String template;
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* Sets the BrowseBean instance to use to retrieve the current Space
*
* @param browseBean BrowseBean instance
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @param nodeService The NodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param permissionService The PermissionService to set.
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* Returns the Space this bean is currently representing
*
* @return The Space Node
*/
public Node getSpace()
{
return this.browseBean.getActionSpace();
}
/**
* Returns the id of the current document
*
* @return The id
*/
public String getId()
{
return getSpace().getId();
}
/**
* Returns the name of the current document
*
* @return Name of the current document
*/
public String getName()
{
return getSpace().getName();
}
/**
* Returns the WebDAV URL for the current document
*
* @return The WebDAV url
*/
public String getWebdavUrl()
{
return Utils.generateURL(FacesContext.getCurrentInstance(), getSpace(), URLMode.WEBDAV);
}
/**
* Returns the URL to access the details page for the current document
*
* @return The bookmark URL
*/
public String getBookmarkUrl()
{
return Utils.generateURL(FacesContext.getCurrentInstance(), getSpace(), URLMode.SHOW_DETAILS);
}
/**
* Returns the CIFS path for the current document
*
* @return The CIFS path
*/
public String getCifsPath()
{
return Utils.generateURL(FacesContext.getCurrentInstance(), getSpace(), URLMode.CIFS);
}
/**
* @return Returns the template Id.
*/
public String getTemplate()
{
// return current template if it exists
NodeRef ref = (NodeRef)getSpace().getProperties().get(ContentModel.PROP_TEMPLATE);
return ref != null ? ref.getId() : this.template;
}
/**
* @param template The template Id to set.
*/
public void setTemplate(String template)
{
this.template = template;
}
/**
* @return true if the current document has the 'templatable' aspect applied and
* references a template that currently exists in the system.
*/
public boolean isTemplatable()
{
NodeRef templateRef = (NodeRef)getSpace().getProperties().get(ContentModel.PROP_TEMPLATE);
return (getSpace().hasAspect(ContentModel.ASPECT_TEMPLATABLE) &&
templateRef != null && nodeService.exists(templateRef));
}
/**
* @return String of the NodeRef for the dashboard template used by the space if any
*/
public String getTemplateRef()
{
NodeRef ref = (NodeRef)getSpace().getProperties().get(ContentModel.PROP_TEMPLATE);
return ref != null ? ref.toString() : null;
}
/**
* Returns a model for use by a template on the Space Details page.
*
* @return model containing current current space info.
*/
public Map getTemplateModel()
{
HashMap model = new HashMap(1, 1.0f);
FacesContext fc = FacesContext.getCurrentInstance();
TemplateNode spaceNode = new TemplateNode(getSpace().getNodeRef(), Repository.getServiceRegistry(fc),
new TemplateImageResolver() {
public String resolveImagePathForName(String filename, boolean small) {
return Utils.getFileTypeImage(filename, small);
}
});
model.put("space", spaceNode);
return model;
}
/**
* @return the list of available Content Templates that can be applied to the current document.
*/
public SelectItem[] getTemplates()
{
// get the template from the special Content Templates folder
FacesContext context = FacesContext.getCurrentInstance();
String xpath = Application.getRootPath(context) + "/" +
Application.getGlossaryFolderName(context) + "/" +
Application.getContentTemplatesFolderName(context) + "//*";
NodeRef rootNodeRef = this.nodeService.getRootNode(Repository.getStoreRef());
NamespaceService resolver = Repository.getServiceRegistry(context).getNamespaceService();
List<NodeRef> results = Repository.getServiceRegistry(context).getSearchService().selectNodes(
rootNodeRef, xpath, null, resolver, false);
List<SelectItem> templates = new ArrayList<SelectItem>(results.size());
if (results.size() != 0)
{
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();
for (NodeRef ref : results)
{
Node childNode = new Node(ref);
if (dd.isSubClass(childNode.getType(), ContentModel.TYPE_CONTENT))
{
templates.add(new SelectItem(childNode.getId(), childNode.getName()));
}
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(templates, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
return templates.toArray(new SelectItem[templates.size()]);
}
// ------------------------------------------------------------------------------
// Action event handlers
/**
* Action handler to apply the selected Template and Templatable aspect to the current Space
*/
public String applyTemplate()
{
try
{
// apply the templatable aspect if required
if (getSpace().hasAspect(ContentModel.ASPECT_TEMPLATABLE) == false)
{
this.nodeService.addAspect(getSpace().getNodeRef(), ContentModel.ASPECT_TEMPLATABLE, null);
}
// get the selected template from the Template Picker
NodeRef templateRef = new NodeRef(Repository.getStoreRef(), this.template);
// set the template NodeRef into the templatable aspect property
this.nodeService.setProperty(getSpace().getNodeRef(), ContentModel.PROP_TEMPLATE, templateRef);
// reset space details for next refresh of details page
getSpace().reset();
}
catch (Exception e)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
}
return OUTCOME_RETURN;
}
/**
* Action handler to remove a dashboard template from the current Space
*/
public String removeTemplate()
{
try
{
// clear template property
this.nodeService.setProperty(getSpace().getNodeRef(), ContentModel.PROP_TEMPLATE, null);
this.nodeService.removeAspect(getSpace().getNodeRef(), ContentModel.ASPECT_TEMPLATABLE);
// reset space details for next refresh of details page
getSpace().reset();
}
catch (Exception e)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
}
return OUTCOME_RETURN;
}
/**
* Navigates to next item in the list of Spaces
*/
public void nextItem(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
List<Node> nodes = this.browseBean.getNodes();
if (nodes.size() > 1)
{
// perform a linear search - this is slow but stateless
// otherwise we would have to manage state of last selected node
// this gets very tricky as this bean is instantiated once and never
// reset - it does not know when the document has changed etc.
for (int i=0; i<nodes.size(); i++)
{
if (id.equals(nodes.get(i).getId()) == true)
{
Node next;
// found our item - navigate to next
if (i != nodes.size() - 1)
{
next = nodes.get(i + 1);
}
else
{
// handle wrapping case
next = nodes.get(0);
}
// prepare for showing details for this node
this.browseBean.setupSpaceAction(next.getId(), false);
}
}
}
}
}
/**
* Navigates to the previous item in the list Spaces
*/
public void previousItem(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
List<Node> nodes = this.browseBean.getNodes();
if (nodes.size() > 1)
{
// see above
for (int i=0; i<nodes.size(); i++)
{
if (id.equals(nodes.get(i).getId()) == true)
{
Node previous;
// found our item - navigate to previous
if (i != 0)
{
previous = nodes.get(i - 1);
}
else
{
// handle wrapping case
previous = nodes.get(nodes.size() - 1);
}
// show details for this node
this.browseBean.setupSpaceAction(previous.getId(), false);
}
}
}
}
}
/**
* Action handler to clear the current Space properties before returning to the browse screen,
* as the user may have modified the properties!
*/
public String closeDialog()
{
this.navigator.resetCurrentNodeProperties();
return "browse";
}
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.repo.component.shelf.UIShortcutsShelfItem;
import org.apache.log4j.Logger;
/**
* This bean manages the user defined list of Recent Spaces in the Shelf component.
*
* @author Kevin Roast
*/
public class UserShortcutsBean
{
private static Logger logger = Logger.getLogger(UserShortcutsBean.class);
/** The NodeService to be used by the bean */
private NodeService nodeService;
/** The BrowseBean reference */
private BrowseBean browseBean;
/** List of shortcut nodes */
private List<Node> shortcuts = null;
private QName QNAME_SHORTCUTS = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "shortcuts");
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @return the List of shortcut Nodes
*/
public List<Node> getShortcuts()
{
if (this.shortcuts == null)
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// get the shortcuts from the preferences for this user
NodeRef prefRef = getShortcutsNodeRef();
List<String> shortcuts = (List<String>)this.nodeService.getProperty(prefRef, QNAME_SHORTCUTS);
if (shortcuts != null)
{
// each shortcut node ID is persisted as a list item in a well known property
this.shortcuts = new ArrayList<Node>(shortcuts.size());
for (int i=0; i<shortcuts.size(); i++)
{
NodeRef ref = new NodeRef(Repository.getStoreRef(), shortcuts.get(i));
if (this.nodeService.exists(ref) == true)
{
Node node = new Node(ref);
// quick init properties while in the usertransaction
node.getProperties();
// save ref to the Node for rendering
this.shortcuts.add(node);
}
else
{
// ignore this shortcut node - no longer exists in the system!
// we write the node list back again afterwards to correct this
if (logger.isDebugEnabled())
logger.debug("Found invalid shortcut node Id: " + ref.getId());
}
}
// if the count of accessable shortcuts is different to our original list then
// write the valid shortcut IDs back to correct invalid node refs
if (this.shortcuts.size() != shortcuts.size())
{
shortcuts = new ArrayList<String>(this.shortcuts.size());
for (int i=0; i<this.shortcuts.size(); i++)
{
shortcuts.add(this.shortcuts.get(i).getId());
}
this.nodeService.setProperty(prefRef, QNAME_SHORTCUTS, (Serializable)shortcuts);
}
}
else
{
this.shortcuts = new ArrayList<Node>(5);
}
tx.commit();
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
return this.shortcuts;
}
/**
* @param spaces List of shortcuts Nodes
*/
public void setShortcuts(List<Node> nodes)
{
this.shortcuts = nodes;
}
// ------------------------------------------------------------------------------
// Action method handlers
/**
* Action handler called when a new shortcut is to be added to the list
*/
public void createShortcut(ActionEvent event)
{
// TODO: add this action to the Details screen for Space and Document
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
try
{
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
Node node = new Node(ref);
boolean foundShortcut = false;
for (int i=0; i<getShortcuts().size(); i++)
{
if (node.getId().equals(getShortcuts().get(i).getId()))
{
// found same node already in the list - so we don't need to add it again
foundShortcut = true;
break;
}
}
if (foundShortcut == false)
{
// add to persistent store
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
NodeRef prefRef = getShortcutsNodeRef();
List<String> shortcuts = (List<String>)this.nodeService.getProperty(prefRef, QNAME_SHORTCUTS);
if (shortcuts == null)
{
shortcuts = new ArrayList<String>(1);
}
shortcuts.add(node.getNodeRef().getId());
this.nodeService.setProperty(prefRef, QNAME_SHORTCUTS, (Serializable)shortcuts);
// commit the transaction
tx.commit();
// add our new shortcut Node to the in-memory list
getShortcuts().add(node);
if (logger.isDebugEnabled())
logger.debug("Added node: " + node.getName() + " to the user shortcuts list.");
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
}
/**
* Get the node we need to store our user preferences
*/
private NodeRef getShortcutsNodeRef()
{
return Application.getCurrentUser(FacesContext.getCurrentInstance()).getUserPreferencesRef();
}
/**
* Action handler bound to the user shortcuts Shelf component called when a node is removed
*/
public void removeShortcut(ActionEvent event)
{
UIShortcutsShelfItem.ShortcutEvent shortcutEvent = (UIShortcutsShelfItem.ShortcutEvent)event;
// remove from persistent store
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
NodeRef prefRef = getShortcutsNodeRef();
List<String> shortcuts = (List<String>)this.nodeService.getProperty(prefRef, QNAME_SHORTCUTS);
if (shortcuts != null && shortcuts.size() > shortcutEvent.Index)
{
// remove the shortcut from the saved list and persist back
shortcuts.remove(shortcutEvent.Index);
this.nodeService.setProperty(prefRef, QNAME_SHORTCUTS, (Serializable)shortcuts);
// commit the transaction
tx.commit();
// remove shortcut Node from the in-memory list
Node node = getShortcuts().remove(shortcutEvent.Index);
if (logger.isDebugEnabled())
logger.debug("Removed node: " + node.getName() + " from the user shortcuts list.");
}
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err);
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
/**
* Action handler bound to the user shortcuts Shelf component called when a node is clicked
*/
public void click(ActionEvent event)
{
// work out which node was clicked from the event data
UIShortcutsShelfItem.ShortcutEvent shortcutEvent = (UIShortcutsShelfItem.ShortcutEvent)event;
Node selectedNode = getShortcuts().get(shortcutEvent.Index);
try
{
DictionaryService dd = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getDictionaryService();
if (dd.isSubClass(selectedNode.getType(), ContentModel.TYPE_FOLDER))
{
// then navigate to the appropriate node in UI
// use browse bean functionality for this as it will update the breadcrumb for us
this.browseBean.updateUILocation(selectedNode.getNodeRef());
}
else if (dd.isSubClass(selectedNode.getType(), ContentModel.TYPE_CONTENT))
{
// view details for document
this.browseBean.setupContentAction(selectedNode.getId(), true);
FacesContext fc = FacesContext.getCurrentInstance();
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "showDocDetails");
}
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {selectedNode.getId()}) );
// remove item from the shortcut list
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
NodeRef prefRef = getShortcutsNodeRef();
List<String> shortcuts = (List<String>)this.nodeService.getProperty(prefRef, QNAME_SHORTCUTS);
if (shortcuts != null && shortcuts.size() > shortcutEvent.Index)
{
// remove the shortcut from the saved list and persist back
shortcuts.remove(shortcutEvent.Index);
this.nodeService.setProperty(prefRef, QNAME_SHORTCUTS, (Serializable)shortcuts);
// commit the transaction
tx.commit();
// remove shortcut Node from the in-memory list
Node node = getShortcuts().remove(shortcutEvent.Index);
if (logger.isDebugEnabled())
logger.debug("Removed deleted node: " + node.getName() + " from the user shortcuts list.");
}
}
catch (Exception err)
{
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
}
}
}

View File

@@ -0,0 +1,336 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.clipboard;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.CopyService;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.repo.component.shelf.UIClipboardShelfItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Kevin Roast
*/
public class ClipboardBean
{
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @return Returns the NodeService.
*/
public NodeService getNodeService()
{
return this.nodeService;
}
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @return Returns the NodeOperationsService.
*/
public CopyService getNodeOperationsService()
{
return this.nodeOperationsService;
}
/**
* @param nodeOperationsService The NodeOperationsService to set.
*/
public void setNodeOperationsService(CopyService nodeOperationsService)
{
this.nodeOperationsService = nodeOperationsService;
}
/**
* @return Returns the navigation bean instance.
*/
public NavigationBean getNavigator()
{
return this.navigator;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @return Returns the clipboard items.
*/
public List<ClipboardItem> getItems()
{
return this.items;
}
/**
* @param items The clipboard items to set.
*/
public void setItems(List<ClipboardItem> items)
{
this.items = items;
}
// ------------------------------------------------------------------------------
// Navigation action event handlers
/**
* Action handler called to add a node to the clipboard for a Copy operation
*/
public void copyNode(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
addClipboardNode(id, ClipboardStatus.COPY);
}
}
/**
* Action handler called to add a node to the clipboard for a Cut operation
*/
public void cutNode(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
addClipboardNode(id, ClipboardStatus.CUT);
}
}
/**
* Action handler call from the browse screen to Paste All clipboard items into the current Space
*/
public void pasteAll(ActionEvent event)
{
performPasteItems(-1);
}
/**
* Action handler called to paste one or all items from the clipboard
*/
public void pasteItem(ActionEvent event)
{
UIClipboardShelfItem.ClipboardEvent clipEvent = (UIClipboardShelfItem.ClipboardEvent)event;
int index = clipEvent.Index;
if (index >= this.items.size())
{
throw new IllegalStateException("Clipboard attempting paste a non existent item index: " + index);
}
performPasteItems(index);
}
/**
* Perform a paste for the specified clipboard item(s)
*
* @param index of clipboard item to paste or -1 for all
*/
private void performPasteItems(int index)
{
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
if (index == -1)
{
// paste all
for (int i=0; i<this.items.size(); i++)
{
performClipboardOperation(this.items.get(i));
}
// remove the cut operation item from the clipboard
List<ClipboardItem> newItems = new ArrayList<ClipboardItem>(this.items.size());
for (int i=0; i<this.items.size(); i++)
{
ClipboardItem item = this.items.get(i);
if (item.Mode != ClipboardStatus.CUT)
{
newItems.add(item);
}
}
setItems(newItems);
// TODO: after a paste all - remove items from the clipboard...? or not. ask linton
}
else
{
// single paste operation
ClipboardItem item = this.items.get(index);
performClipboardOperation(item);
if (item.Mode == ClipboardStatus.CUT)
{
this.items.remove(index);
}
}
// commit the transaction
tx.commit();
// refresh UI on success
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
catch (Exception err)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_PASTE) + err.getMessage(), err);
}
}
/**
* Perform the operation for the specified clipboard item
*
* @param item
*/
private void performClipboardOperation(ClipboardItem item)
{
NodeRef parentRef = new NodeRef(Repository.getStoreRef(),
this.navigator.getCurrentNodeId());
// TODO: should we use primary parent here?
// The problem is we can't pass round ChildAssocRefs as form params etc. in the UI!
// It's tricky if we need to pass childassocref around everywhere...!
ChildAssociationRef assocRef = this.nodeService.getPrimaryParent(item.Node.getNodeRef());
if (item.Mode == ClipboardStatus.COPY)
{
if (logger.isDebugEnabled())
logger.debug("Trying to copy node ID: " + item.Node.getId() + " into node ID: " + parentRef.getId());
// call the node ops service to initiate the copy
// TODO: should the assoc qname be derived from the type...?
DictionaryService dd = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getDictionaryService();
boolean copyChildren = dd.isSubClass(item.Node.getType(), ContentModel.TYPE_FOLDER);
NodeRef copyRef = this.nodeOperationsService.copy(
item.Node.getNodeRef(),
parentRef,
ContentModel.ASSOC_CONTAINS,
assocRef.getQName(),
copyChildren);
}
else
{
if (logger.isDebugEnabled())
logger.debug("Trying to move node ID: " + item.Node.getId() + " into node ID: " + parentRef.getId());
// move the node
this.nodeService.moveNode(
item.Node.getNodeRef(),
parentRef,
ContentModel.ASSOC_CONTAINS,
assocRef.getQName());
}
}
/**
* Add a clipboard node for an operation to the clipboard
*
* @param id ID of the node for the operation
* @param mode ClipboardStatus for the operation
*/
private void addClipboardNode(String id, ClipboardStatus mode)
{
try
{
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
// check for duplicates first
ClipboardItem item = new ClipboardItem(new Node(ref), mode);
boolean foundDuplicate = false;
for (int i=0; i<items.size(); i++)
{
if (items.get(i).equals(item))
{
// found a duplicate replace with new instance as copy mode may have changed
items.set(i, item);
foundDuplicate = true;
break;
}
}
// if duplicate not found, then append to list
if (foundDuplicate == false)
{
items.add(item);
}
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] {id}) );
}
}
// ------------------------------------------------------------------------------
// Private data
private static Log logger = LogFactory.getLog(ClipboardBean.class);
/** I18N messages */
private static final String MSG_ERROR_PASTE = "error_paste";
/** The NodeService to be used by the bean */
private NodeService nodeService;
/** The NodeOperationsService to be used by the bean */
private CopyService nodeOperationsService;
/** The NavigationBean reference */
private NavigationBean navigator;
/** Current state of the clipboard items */
private List<ClipboardItem> items = new ArrayList<ClipboardItem>(4);
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.clipboard;
import org.alfresco.web.bean.repository.Node;
/**
* @author Kevin Roast
*/
public class ClipboardItem
{
/**
* Constructor
*
* @param node The node on the clipboard
* @param mode The ClipboardStatus enum value
*/
public ClipboardItem(Node node, ClipboardStatus mode)
{
this.Node = node;
this.Mode = mode;
}
/**
* Override equals() to compare NodeRefs
*/
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
if (obj instanceof ClipboardItem)
{
return ((ClipboardItem)obj).Node.getNodeRef().equals(Node.getNodeRef());
}
else
{
return false;
}
}
/**
* Override hashCode() to use the internal NodeRef hashcode instead
*/
public int hashCode()
{
return Node.getNodeRef().hashCode();
}
public Node Node;
public ClipboardStatus Mode;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.clipboard;
/**
* @author Kevin Roast
*
* Clipboard status for an item.
*/
public enum ClipboardStatus {CUT, COPY}

View File

@@ -0,0 +1,258 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.preview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
/**
* Backing bean for the Preview Document in Template action page
*
* @author Kevin Roast
*/
public abstract class BasePreviewBean
{
private static final String NO_SELECTION = "none";
/** BrowseBean instance */
protected BrowseBean browseBean;
/** NodeService instance */
protected NodeService nodeService;
/** The SearchService instance */
protected SearchService searchService;
/** The NavigationBean bean reference */
protected NavigationBean navigator;
protected NodeRef template;
/**
* @param nodeService The nodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @param searchService The searchService to set.
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* Returns the node this bean is currently working with
*
* @return The current Node
*/
public abstract Node getNode();
/**
* Returns the id of the current node
*
* @return The id
*/
public String getId()
{
return getNode().getId();
}
/**
* Returns the name of the current node
*
* @return Name of the current node
*/
public String getName()
{
return getNode().getName();
}
/**
* @return the list of available Content Templates that can be applied to the current document.
*/
public SelectItem[] getTemplates()
{
// TODO: could cache this last for say 1 minute before requerying
// get the template from the special Content Templates folder
FacesContext context = FacesContext.getCurrentInstance();
String xpath = Application.getRootPath(context) + "/" +
Application.getGlossaryFolderName(context) + "/" +
Application.getContentTemplatesFolderName(context) + "//*";
NodeRef rootNodeRef = this.nodeService.getRootNode(Repository.getStoreRef());
NamespaceService resolver = Repository.getServiceRegistry(context).getNamespaceService();
List<NodeRef> results = this.searchService.selectNodes(rootNodeRef, xpath, null, resolver, false);
List<SelectItem> templates = new ArrayList<SelectItem>(results.size());
if (results.size() != 0)
{
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();
for (NodeRef ref : results)
{
Node childNode = new Node(ref);
if (dd.isSubClass(childNode.getType(), ContentModel.TYPE_CONTENT))
{
templates.add(new SelectItem(childNode.getId(), childNode.getName()));
}
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(templates, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
// add an entry (at the start) to instruct the user to select a template
templates.add(0, new SelectItem(NO_SELECTION, Application.getMessage(FacesContext.getCurrentInstance(), "select_a_template")));
return templates.toArray(new SelectItem[templates.size()]);
}
/**
* Returns a model for use by the template on the Preview page.
*
* @return model containing current document/space info.
*/
public abstract Map getTemplateModel();
/** Template Image resolver helper */
protected TemplateImageResolver imageResolver = new TemplateImageResolver()
{
public String resolveImagePathForName(String filename, boolean small)
{
return Utils.getFileTypeImage(filename, small);
}
};
/**
* @return the current template as a full NodeRef
*/
public NodeRef getTemplateRef()
{
return this.template;
}
/**
* @return Returns the template Id.
*/
public String getTemplate()
{
return (this.template != null ? this.template.getId() : null);
}
/**
* @param template The template Id to set.
*/
public void setTemplate(String template)
{
if (template != null && template.equals(NO_SELECTION) == false)
{
this.template = new NodeRef(Repository.getStoreRef(), template);
}
}
private int findNextPreviewNode(List<Node> nodes, int start)
{
// search from start to end of list
for (int i=start; i<nodes.size(); i++)
{
Node next = nodes.get(i);
if (next.hasAspect(ContentModel.ASPECT_TEMPLATABLE))
{
return i;
}
}
// search from zero index to start - 1 (to skip original node)
for (int i=0; i<start - 1; i++)
{
Node next = nodes.get(i);
if (next.hasAspect(ContentModel.ASPECT_TEMPLATABLE))
{
return i;
}
}
return -1;
}
private int findPrevPreviewNode(List<Node> nodes, int start)
{
// search from start to beginning of list
for (int i=start; i>=0; i--)
{
Node next = nodes.get(i);
if (next.hasAspect(ContentModel.ASPECT_TEMPLATABLE))
{
return i;
}
}
// end of list to start + 1 (to skip original node)
for (int i=nodes.size() - 1; i>start; i--)
{
Node next = nodes.get(i);
if (next.hasAspect(ContentModel.ASPECT_TEMPLATABLE))
{
return i;
}
}
return -1;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.preview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
/**
* Backing bean for the Preview Document in Template action page
*
* @author Kevin Roast
*/
public class DocumentPreviewBean extends BasePreviewBean
{
/**
* Returns the document this bean is currently representing
*
* @return The document Node
*/
public Node getNode()
{
return this.browseBean.getDocument();
}
/**
* Returns a model for use by a template on the Document Details page.
*
* @return model containing current document and current space info.
*/
public Map getTemplateModel()
{
HashMap model = new HashMap(3, 1.0f);
FacesContext fc = FacesContext.getCurrentInstance();
TemplateNode documentNode = new TemplateNode(getNode().getNodeRef(),
Repository.getServiceRegistry(fc), imageResolver);
model.put("document", documentNode);
TemplateNode spaceNode = new TemplateNode(this.navigator.getCurrentNode().getNodeRef(),
Repository.getServiceRegistry(fc), imageResolver);
model.put("space", spaceNode);
return model;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.preview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
/**
* Backing bean for the Preview Space in Template action page
*
* @author Kevin Roast
*/
public class SpacePreviewBean extends BasePreviewBean
{
/**
* Returns the Space this bean is currently representing
*
* @return The Space Node
*/
public Node getNode()
{
return this.browseBean.getActionSpace();
}
/**
* Returns a model for use by a template on the Document Details page.
*
* @return model containing current document and current space info.
*/
public Map getTemplateModel()
{
HashMap model = new HashMap(3, 1.0f);
FacesContext fc = FacesContext.getCurrentInstance();
TemplateNode spaceNode = new TemplateNode(getNode().getNodeRef(),
Repository.getServiceRegistry(fc), imageResolver);
model.put("space", spaceNode);
return model;
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Lighweight client side representation of the repository data dictionary.
* This allows service calls to be kept to a minimum and for bean access, thus enabling JSF
* value binding expressions.
*
* @author gavinc
*/
public final class DataDictionary
{
private static Log logger = LogFactory.getLog(DataDictionary.class);
private DictionaryService dictionaryService;
private NamespaceService namespaceService;
private Map<QName, TypeDefinition> types = new HashMap<QName, TypeDefinition>(11, 1.0f);
/**
* Constructor
*
* @param dictionaryService The dictionary service to use to retrieve the data
*/
public DataDictionary(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* Returns the type definition for the type represented by the given qname
*
* @param type The qname of the type to lookup the definition for
* @return The type definition for the requested type
*/
public TypeDefinition getTypeDef(QName type)
{
TypeDefinition typeDef = types.get(type);
if (typeDef == null)
{
typeDef = this.dictionaryService.getType(type);
if (typeDef != null)
{
types.put(type, typeDef);
}
}
return typeDef;
}
/**
* Returns the type definition for the type represented by the given qname
* and for all the given aspects
*
* @param type The type to retrieve the definition for
* @param optionalAspects A list of aspects to retrieve the definition for
* @return A unified type definition of the given type and aspects
*/
public TypeDefinition getTypeDef(QName type, Collection<QName> optionalAspects)
{
return this.dictionaryService.getAnonymousType(type, optionalAspects);
}
/**
* Returns the property definition for the given property on the given node
*
* @param node The node from which to get the property
* @param property The property to find the definition for
* @return The property definition or null if the property is not known
*/
public PropertyDefinition getPropertyDefinition(Node node, String property)
{
PropertyDefinition propDef = null;
TypeDefinition typeDef = getTypeDef(node.getType(), node.getAspects());
if (typeDef != null)
{
Map<QName, PropertyDefinition> properties = typeDef.getProperties();
propDef = properties.get(Repository.resolveToQName(property));
}
return propDef;
}
/**
* Returns the association definition for the given association on the given node
*
* @param node The node from which to get the association
* @param association The association to find the definition for
* @return The association definition or null if the association is not known
*/
public AssociationDefinition getAssociationDefinition(Node node, String association)
{
AssociationDefinition assocDef = null;
TypeDefinition typeDef = getTypeDef(node.getType(), node.getAspects());
if (typeDef != null)
{
Map<QName, AssociationDefinition> assocs = typeDef.getAssociations();
assocDef = assocs.get(Repository.resolveToQName(association));
}
return assocDef;
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.repository;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
/**
* Lighweight client side representation of a node held in the repository, which
* is modelled as a map for use in the data tables.
*
* @author gavinc
*/
public class MapNode extends Node implements Map<String, Object>
{
private static final long serialVersionUID = 4051322327734433079L;
private boolean propsInitialised = false;
/**
* Constructor
*
* @param nodeRef The NodeRef this Node wrapper represents
*/
public MapNode(NodeRef nodeRef)
{
super(nodeRef);
}
/**
* Constructor
*
* @param nodeRef The NodeRef this Node wrapper represents
* @param nodeService The node service to use to retrieve data for this node
* @param initProps True to immediately init the properties of the node, false to do nothing
*/
public MapNode(NodeRef nodeRef, NodeService nodeService, boolean initProps)
{
super(nodeRef);
if (initProps == true)
{
getProperties();
}
}
// ------------------------------------------------------------------------------
// Map implementation - allows the Node bean to be accessed using JSF expression syntax
/**
* @see java.util.Map#clear()
*/
public void clear()
{
getProperties().clear();
}
/**
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key)
{
return getProperties().containsKey(key);
}
/**
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value)
{
return getProperties().containsKey(value);
}
/**
* @see java.util.Map#entrySet()
*/
public Set entrySet()
{
return getProperties().entrySet();
}
/**
* @see java.util.Map#get(java.lang.Object)
*/
public Object get(Object key)
{
Object obj = null;
// there are some things that aren't available as properties
// but from method calls, so for these handle them individually
Map<String, Object> props = getProperties();
if (propsInitialised == false)
{
// well known properties required as publically accessable map attributes
props.put("id", this.getId());
props.put("name", this.getName()); // TODO: perf test pulling back single prop here instead of all!
props.put("nodeRef", this.getNodeRef());
propsInitialised = true;
}
return props.get(key);
}
/**
* @see java.util.Map#isEmpty()
*/
public boolean isEmpty()
{
return getProperties().isEmpty();
}
/**
* @see java.util.Map#keySet()
*/
public Set keySet()
{
return getProperties().keySet();
}
/**
* @see java.util.Map#put(K, V)
*/
public Object put(String key, Object value)
{
return getProperties().put(key, value);
}
/**
* @see java.util.Map#putAll(java.util.Map)
*/
public void putAll(Map t)
{
getProperties().putAll(t);
}
/**
* @see java.util.Map#remove(java.lang.Object)
*/
public Object remove(Object key)
{
return getProperties().remove(key);
}
/**
* @see java.util.Map#size()
*/
public int size()
{
return getProperties().size();
}
/**
* @see java.util.Map#values()
*/
public Collection values()
{
return getProperties().values();
}
}

View File

@@ -0,0 +1,426 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.repository;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.context.FacesContext;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Lighweight client side representation of a node held in the repository.
*
* @author gavinc
*/
public class Node implements Serializable
{
private static final long serialVersionUID = 3544390322739034169L;
protected static Log logger = LogFactory.getLog(Node.class);
protected NodeRef nodeRef;
private String name;
private QName type;
private String path;
private String id;
private Set<QName> aspects = null;
private Map<String, Boolean> permissions;
protected QNameNodeMap<String, Object> properties;
protected boolean propsRetrieved = false;
protected ServiceRegistry services = null;
private boolean childAssocsRetrieved = false;
private QNameNodeMap childAssociations;
private Map<String, Map<String, ChildAssociationRef>> childAssociationsAdded;
private Map<String, Map<String, ChildAssociationRef>> childAssociationsRemoved;
private boolean assocsRetrieved = false;
private QNameNodeMap associations;
private Map<String, Map<String, AssociationRef>> associationsAdded;
private Map<String, Map<String, AssociationRef>> associationsRemoved;
/**
* Constructor
*
* @param nodeRef The NodeRef this Node wrapper represents
*/
public Node(NodeRef nodeRef)
{
if (nodeRef == null)
{
throw new IllegalArgumentException("NodeRef must be supplied for creation of a Node.");
}
this.nodeRef = nodeRef;
this.id = nodeRef.getId();
this.properties = new QNameNodeMap<String, Object>(getServiceRegistry().getNamespaceService(), this);
}
/**
* @return All the properties known about this node.
*/
public Map<String, Object> getProperties()
{
if (this.propsRetrieved == false)
{
Map<QName, Serializable> props = getServiceRegistry().getNodeService().getProperties(this.nodeRef);
for (QName qname: props.keySet())
{
Serializable propValue = props.get(qname);
this.properties.put(qname.toString(), propValue);
}
this.propsRetrieved = true;
}
return this.properties;
}
/**
* @return All the associations this node has as a Map, using the association
* type as the key
*/
public final Map getAssociations()
{
if (this.assocsRetrieved == false)
{
associations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this);
List<AssociationRef> assocs = getServiceRegistry().getNodeService().getTargetAssocs(this.nodeRef, RegexQNamePattern.MATCH_ALL);
for (AssociationRef assocRef: assocs)
{
String assocName = assocRef.getTypeQName().toString();
List list = (List)this.associations.get(assocName);
// create the list if this is first association with 'assocName'
if (list == null)
{
list = new ArrayList<AssociationRef>();
this.associations.put(assocName, list);
}
// add the association to the list
list.add(assocRef);
}
this.assocsRetrieved = true;
}
return this.associations;
}
/**
* Returns all the associations added to this node in this UI session
*
* @return Map of Maps of AssociationRefs
*/
public final Map<String, Map<String, AssociationRef>> getAddedAssociations()
{
if (this.associationsAdded == null)
{
this.associationsAdded = new HashMap<String, Map<String, AssociationRef>>();
}
return this.associationsAdded;
}
/**
* Returns all the associations removed from this node is this UI session
*
* @return Map of Maps of AssociationRefs
*/
public final Map<String, Map<String, AssociationRef>> getRemovedAssociations()
{
if (this.associationsRemoved == null)
{
this.associationsRemoved = new HashMap<String, Map<String, AssociationRef>>();
}
return this.associationsRemoved;
}
/**
* @return All the child associations this node has as a Map, using the association
* type as the key
*/
public final Map getChildAssociations()
{
if (this.childAssocsRetrieved == false)
{
this.childAssociations = new QNameNodeMap(getServiceRegistry().getNamespaceService(), this);
List<ChildAssociationRef> assocs = getServiceRegistry().getNodeService().getChildAssocs(this.nodeRef);
for (ChildAssociationRef assocRef: assocs)
{
String assocName = assocRef.getTypeQName().toString();
List list = (List)this.childAssociations.get(assocName);
// create the list if this is first association with 'assocName'
if (list == null)
{
list = new ArrayList<ChildAssociationRef>();
this.childAssociations.put(assocName, list);
}
// add the association to the list
list.add(assocRef);
}
this.childAssocsRetrieved = true;
}
return this.childAssociations;
}
/**
* Returns all the child associations added to this node in this UI session
*
* @return Map of Maps of ChildAssociationRefs
*/
public final Map<String, Map<String, ChildAssociationRef>> getAddedChildAssociations()
{
if (this.childAssociationsAdded == null)
{
this.childAssociationsAdded = new HashMap<String, Map<String, ChildAssociationRef>>();
}
return this.childAssociationsAdded;
}
/**
* Returns all the child associations removed from this node is this UI session
*
* @return Map of Maps of ChildAssociationRefs
*/
public final Map<String, Map<String, ChildAssociationRef>> getRemovedChildAssociations()
{
if (this.childAssociationsRemoved == null)
{
this.childAssociationsRemoved = new HashMap<String, Map<String, ChildAssociationRef>>();
}
return this.childAssociationsRemoved;
}
/**
* Register a property resolver for the named property.
*
* @param name Name of the property this resolver is for
* @param resolver Property resolver to register
*/
public final void addPropertyResolver(String name, NodePropertyResolver resolver)
{
this.properties.addPropertyResolver(name, resolver);
}
/**
* Determines whether the given property name is held by this node
*
* @param propertyName Property to test existence of
* @return true if property exists, false otherwise
*/
public final boolean hasProperty(String propertyName)
{
return getProperties().containsKey(propertyName);
}
/**
* @return Returns the NodeRef this Node object represents
*/
public final NodeRef getNodeRef()
{
return this.nodeRef;
}
/**
* @return Returns the type.
*/
public final QName getType()
{
if (this.type == null)
{
this.type = getServiceRegistry().getNodeService().getType(this.nodeRef);
}
return type;
}
/**
* @return The display name for the node
*/
public final String getName()
{
if (this.name == null)
{
// try and get the name from the properties first
this.name = (String)getProperties().get("cm:name");
// if we didn't find it as a property get the name from the association name
if (this.name == null)
{
this.name = getServiceRegistry().getNodeService().getPrimaryParent(this.nodeRef).getQName().getLocalName();
}
}
return this.name;
}
/**
* @return The list of aspects applied to this node
*/
public final Set<QName> getAspects()
{
if (this.aspects == null)
{
this.aspects = getServiceRegistry().getNodeService().getAspects(this.nodeRef);
}
return this.aspects;
}
/**
* @param aspect The aspect to test for
* @return true if the node has the aspect false otherwise
*/
public final boolean hasAspect(QName aspect)
{
Set aspects = getAspects();
return aspects.contains(aspect);
}
/**
* Return whether the current user has the specified access permission on this Node
*
* @param permission Permission to validate against
*
* @return true if the permission is applied to the node for this user, false otherwise
*/
public final boolean hasPermission(String permission)
{
Boolean valid = null;
if (permissions != null)
{
valid = permissions.get(permission);
}
else
{
permissions = new HashMap<String, Boolean>(5, 1.0f);
}
if (valid == null)
{
PermissionService service = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getPermissionService();
valid = Boolean.valueOf(service.hasPermission(this.nodeRef, permission) == AccessStatus.ALLOWED);
permissions.put(permission, valid);
}
return valid.booleanValue();
}
/**
* @return The GUID for the node
*/
public final String getId()
{
return this.id;
}
/**
* @return The path for the node
*/
public final String getPath()
{
if (this.path == null)
{
this.path = getServiceRegistry().getNodeService().getPath(this.nodeRef).toString();
}
return this.path;
}
/**
* Resets the state of the node to force re-retrieval of the data
*/
public void reset()
{
this.name = null;
this.type = null;
this.path = null;
this.properties.clear();
this.propsRetrieved = false;
this.aspects = null;
this.permissions = null;
this.associations = null;
this.associationsAdded = null;
this.associationsRemoved = null;
this.assocsRetrieved = false;
this.childAssociations = null;
this.childAssociationsAdded = null;
this.childAssociationsRemoved = null;
this.childAssocsRetrieved = false;
}
/**
* Override Object.toString() to provide useful debug output
*/
public String toString()
{
if (getServiceRegistry().getNodeService() != null)
{
if (getServiceRegistry().getNodeService().exists(nodeRef))
{
return "Node Type: " + getType() +
"\nNode Properties: " + this.getProperties().toString() +
"\nNode Aspects: " + this.getAspects().toString();
}
else
{
return "Node no longer exists: " + nodeRef;
}
}
else
{
return super.toString();
}
}
protected ServiceRegistry getServiceRegistry()
{
if (this.services == null)
{
this.services = Repository.getServiceRegistry(FacesContext.getCurrentInstance());
}
return this.services;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.repository;
/**
* Simple interface used to implement small classes capable of calculating dynamic property values
* for Nodes at runtime. This allows bean responsible for building large lists of Nodes to
* encapsulate the code needed to retrieve non-standard Node properties. The values are then
* calculated on demand by the property resolver.
*
* When a node is reset() the standard and other props are cleared. If property resolvers are used
* then the non-standard props will be restored automatically as well.
*
* @author Kevin Roast
*/
public interface NodePropertyResolver
{
/**
* Get the property value for this resolver
*
* @param node Node this property is for
*
* @return property value
*/
public Object get(Node node);
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QNameMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A extension of the repo QNameMap to provide custom property resolving support for Node wrappers.
*
* @author Kevin Roast
*/
public final class QNameNodeMap<K,V> extends QNameMap implements Map, Cloneable
{
private Node parent = null;
private Map<String, NodePropertyResolver> resolvers = new HashMap<String, NodePropertyResolver>(11, 1.0f);
/**
* Constructor
*
* @param parent Parent Node of the QNameNodeMap
*/
public QNameNodeMap(NamespacePrefixResolver resolver, Node parent)
{
super(resolver);
if (parent == null)
{
throw new IllegalArgumentException("Parent Node cannot be null!");
}
this.parent = parent;
}
/**
* Register a property resolver for the named property.
*
* @param name Name of the property this resolver is for
* @param resolver Property resolver to register
*/
public void addPropertyResolver(String name, NodePropertyResolver resolver)
{
this.resolvers.put(name, resolver);
}
/**
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key)
{
return (this.contents.containsKey(Repository.resolveToQNameString((String)key)) ||
this.resolvers.containsKey(key));
}
/**
* @see java.util.Map#get(java.lang.Object)
*/
public Object get(Object key)
{
String qnameKey = Repository.resolveToQNameString(key.toString());
Object obj = this.contents.get(qnameKey);
if (obj == null)
{
// if a property resolver exists for this property name then invoke it
NodePropertyResolver resolver = this.resolvers.get(key.toString());
if (resolver != null)
{
obj = resolver.get(this.parent);
// cache the result
// obviously the cache is useless if the result is null, in most cases it shouldn't be
this.contents.put(qnameKey, obj);
}
}
return obj;
}
/**
* Perform a get without using property resolvers
*
* @param key item key
* @return object
*/
public Object getRaw(Object key)
{
return this.contents.get(Repository.resolveToQNameString((String)key));
}
/**
* Shallow copy the map by copying keys and values into a new QNameNodeMap
*/
public Object clone()
{
QNameNodeMap map = new QNameNodeMap(this.resolver, this.parent);
map.putAll(this);
if (this.resolvers.size() != 0)
{
map.resolvers = (Map)((HashMap)this.resolvers).clone();
}
return map;
}
}

View File

@@ -0,0 +1,620 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.repository;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.transaction.UserTransaction;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.configuration.ConfigurableService;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.metadata.MetadataExtracter;
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.web.app.Application;
import org.alfresco.web.ui.common.Utils;
import org.apache.log4j.Logger;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Helper class for accessing repository objects, convert values, escape values and service utilities.
*
* @author gavinc
* @author kevinr
*/
public final class Repository
{
/** I18N error messages */
public static final String ERROR_NODEREF = "error_noderef";
public static final String ERROR_GENERIC = "error_generic";
public static final String ERROR_NOHOME = "error_homespace";
public static final String ERROR_SEARCH = "error_search";
private static final String METADATA_EXTACTER_REGISTRY = "metadataExtracterRegistry";
private static Logger logger = Logger.getLogger(Repository.class);
/** cache of client StoreRef */
private static StoreRef storeRef = null;
/** reference to Person folder */
private static NodeRef peopleRef = null;
/** reference to System folder */
private static NodeRef systemRef = null;
/** reference to the namespace service */
private static NamespaceService namespaceService = null;
/**
* Private constructor
*/
private Repository()
{
}
/**
* Returns a store reference object
*
* @return A StoreRef object
*/
public static StoreRef getStoreRef()
{
return storeRef;
}
/**
* Returns a store reference object.
* This method is used to setup the cached value by the ContextListener initialisation methods
*
* @return The StoreRef object
*/
public static StoreRef getStoreRef(ServletContext context)
{
storeRef = Application.getRepositoryStoreRef(context);
return storeRef;
}
/**
* Helper to get the display name for a Node.
* The method will attempt to use the "name" attribute, if not found it will revert to using
* the QName.getLocalName() retrieved from the primary parent relationship.
*
* @param ref NodeRef
*
* @return display name string for the specified Node.
*/
public static String getNameForNode(NodeService nodeService, NodeRef ref)
{
String name = null;
// try to find a display "name" property for this node
Object nameProp = nodeService.getProperty(ref, ContentModel.PROP_NAME);
if (nameProp != null)
{
name = nameProp.toString();
}
else
{
// revert to using QName if not found
QName qname = nodeService.getPrimaryParent(ref).getQName();
if (qname != null)
{
name = qname.getLocalName();
}
}
return name;
}
/**
* Escape a QName value so it can be used in lucene search strings
*
* @param qName QName to escape
*
* @return escaped value
*/
public static String escapeQName(QName qName)
{
String string = qName.toString();
StringBuilder buf = new StringBuilder(string.length() + 4);
for (int i = 0; i < string.length(); i++)
{
char c = string.charAt(i);
if ((c == '{') || (c == '}') || (c == ':') || (c == '-'))
{
buf.append('\\');
}
buf.append(c);
}
return buf.toString();
}
/**
* Return whether a Node is currently locked
*
* @param node The Node wrapper to test against
* @param lockService The LockService to use
*
* @return whether a Node is currently locked
*/
public static Boolean isNodeLocked(Node node, LockService lockService)
{
Boolean locked = Boolean.FALSE;
if (node.hasAspect(ContentModel.ASPECT_LOCKABLE))
{
LockStatus lockStatus = lockService.getLockStatus(node.getNodeRef());
if (lockStatus == LockStatus.LOCKED || lockStatus == LockStatus.LOCK_OWNER)
{
locked = Boolean.TRUE;
}
}
return locked;
}
/**
* Return whether a Node is currently locked by the current user
*
* @param node The Node wrapper to test against
* @param lockService The LockService to use
*
* @return whether a Node is currently locked by the current user
*/
public static Boolean isNodeOwnerLocked(Node node, LockService lockService)
{
Boolean locked = Boolean.FALSE;
if (node.hasAspect(ContentModel.ASPECT_LOCKABLE) &&
lockService.getLockStatus(node.getNodeRef()) == LockStatus.LOCK_OWNER)
{
locked = Boolean.TRUE;
}
return locked;
}
/**
* Return whether a WorkingCopy Node is owned by the current User
*
* @param node The Node wrapper to test against
* @param lockService The LockService to use
*
* @return whether a WorkingCopy Node is owned by the current User
*/
public static Boolean isNodeOwner(Node node, LockService lockService)
{
Boolean locked = Boolean.FALSE;
if (node.hasAspect(ContentModel.ASPECT_WORKING_COPY))
{
Object obj = node.getProperties().get("workingCopyOwner");
if (obj instanceof String)
{
User user = Application.getCurrentUser(FacesContext.getCurrentInstance());
if ( ((String)obj).equals(user.getUserName()))
{
locked = Boolean.TRUE;
}
}
}
return locked;
}
/**
* Return the human readable form of the specified node Path. Fast version of the method that
* simply converts QName localname components to Strings.
*
* @param path Path to extract readable form from, excluding the final element
*
* @return human readable form of the Path excluding the final element
*/
public static String getDisplayPath(Path path)
{
StringBuilder buf = new StringBuilder(64);
for (int i=0; i<path.size()-1; i++)
{
String elementString = null;
Path.Element element = path.get(i);
if (element instanceof Path.ChildAssocElement)
{
ChildAssociationRef elementRef = ((Path.ChildAssocElement)element).getRef();
if (elementRef.getParentRef() != null)
{
elementString = elementRef.getQName().getLocalName();
}
}
else
{
elementString = element.getElementString();
}
if (elementString != null)
{
buf.append("/");
buf.append(elementString);
}
}
return buf.toString();
}
/**
* Resolve a Path by converting each element into its display NAME attribute
*
* @param path Path to convert
* @param separator Separator to user between path elements
* @param prefix To prepend to the path
*
* @return Path converted using NAME attribute on each element
*/
public static String getNamePath(NodeService nodeService, Path path, NodeRef rootNode, String separator, String prefix)
{
StringBuilder buf = new StringBuilder(128);
// ignore root node check if not passed in
boolean foundRoot = (rootNode == null);
buf.append(prefix);
// skip first element as it represents repo root '/'
for (int i=1; i<path.size(); i++)
{
Path.Element element = path.get(i);
String elementString = null;
if (element instanceof Path.ChildAssocElement)
{
ChildAssociationRef elementRef = ((Path.ChildAssocElement)element).getRef();
if (elementRef.getParentRef() != null)
{
// only append if we've found the root already
if (foundRoot == true)
{
Object nameProp = nodeService.getProperty(elementRef.getChildRef(), ContentModel.PROP_NAME);
if (nameProp != null)
{
elementString = nameProp.toString();
}
else
{
elementString = element.getElementString();
}
}
// either we've found root already or may have now
// check after as we want to skip the root as it represents the CIFS share name
foundRoot = (foundRoot || elementRef.getChildRef().equals(rootNode));
}
}
else
{
elementString = element.getElementString();
}
if (elementString != null)
{
buf.append(separator);
buf.append(elementString);
}
}
return buf.toString();
}
/**
* Return the mimetype code for the specified file name.
* <p>
* The file extension will be extracted from the filename and used to lookup the mimetype.
*
* @param context FacesContext
* @param filename Non-null filename to process
*
* @return mimetype for the specified filename - falls back to 'application/octet-stream' if not found.
*/
public static String getMimeTypeForFileName(FacesContext context, String filename)
{
// base the mimetype from the file extension
MimetypeService mimetypeService = (MimetypeService)getServiceRegistry(context).getMimetypeService();
// fall back to binary mimetype if no match found
String mimetype = MimetypeMap.MIMETYPE_BINARY;
int extIndex = filename.lastIndexOf('.');
if (extIndex != -1)
{
String ext = filename.substring(extIndex + 1).toLowerCase();
String mt = mimetypeService.getMimetypesByExtension().get(ext);
if (mt != null)
{
mimetype = mt;
}
}
return mimetype;
}
/**
* Return a UserTransaction instance
*
* @param context FacesContext
*
* @return UserTransaction
*/
public static UserTransaction getUserTransaction(FacesContext context)
{
TransactionService transactionService = getServiceRegistry(context).getTransactionService();
return transactionService.getUserTransaction();
}
/**
* Return a UserTransaction instance
*
* @param context FacesContext
* @param readonly Transaction readonly state
*
* @return UserTransaction
*/
public static UserTransaction getUserTransaction(FacesContext context, boolean readonly)
{
TransactionService transactionService = getServiceRegistry(context).getTransactionService();
return transactionService.getUserTransaction(readonly);
}
/**
* Return the Repository Service Registry
*
* @param context Faces Context
* @return the Service Registry
*/
public static ServiceRegistry getServiceRegistry(FacesContext context)
{
return (ServiceRegistry)FacesContextUtils.getRequiredWebApplicationContext(
context).getBean(ServiceRegistry.SERVICE_REGISTRY);
}
/**
* Return the Repository Service Registry
*
* @param context Servlet Context
* @return the Service Registry
*/
public static ServiceRegistry getServiceRegistry(ServletContext context)
{
return (ServiceRegistry)WebApplicationContextUtils.getRequiredWebApplicationContext(
context).getBean(ServiceRegistry.SERVICE_REGISTRY);
}
/**
* Return the Configurable Service
*
* @return the configurable service
*/
public static ConfigurableService getConfigurableService(FacesContext context)
{
return (ConfigurableService)FacesContextUtils.getRequiredWebApplicationContext(context).getBean("configurableService");
}
/**
* Return the Metadata Extracter Registry
*
* @param context Faces Context
* @return the MetadataExtracterRegistry
*/
public static MetadataExtracterRegistry getMetadataExtracterRegistry(FacesContext context)
{
return (MetadataExtracterRegistry)FacesContextUtils.getRequiredWebApplicationContext(
context).getBean(METADATA_EXTACTER_REGISTRY);
}
/**
* Extracts the metadata of a "raw" piece of content into a map.
*
* @param context Faces Context
* @param reader Content reader for the source content to extract from
* @param destination Map of metadata to set metadata values into
* @return True if an extracter was found
*/
public static boolean extractMetadata(FacesContext context, ContentReader reader, Map<QName, Serializable> destination)
{
// check that source mimetype is available
String mimetype = reader.getMimetype();
if (mimetype == null)
{
throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader);
}
// look for a transformer
MetadataExtracter extracter = getMetadataExtracterRegistry(context).getExtracter(mimetype);
if (extracter == null)
{
// No metadata extracter is not a failure, but we flag it
return false;
}
// we have a transformer, so do it
extracter.extract(reader, destination);
return true;
}
/**
* Query a list of Person type nodes from the repo
* It is currently assumed that all Person nodes exist below the Repository root node
*
* @param context Faces Context
* @param nodeService The node service
* @param searchService used to perform the search
* @return List of Person node objects
*/
public static List<Node> getUsers(FacesContext context, NodeService nodeService, SearchService searchService)
{
List<Node> personNodes = null;
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
PersonService personService = (PersonService)FacesContextUtils.getRequiredWebApplicationContext(context).getBean("personService");
NodeRef peopleRef = personService.getPeopleContainer();
// TODO: better to perform an XPath search or a get for a specific child type here?
List<ChildAssociationRef> childRefs = nodeService.getChildAssocs(peopleRef);
personNodes = new ArrayList<Node>(childRefs.size());
for (ChildAssociationRef ref: childRefs)
{
// create our Node representation from the NodeRef
NodeRef nodeRef = ref.getChildRef();
if (nodeService.getType(nodeRef).equals(ContentModel.TYPE_PERSON))
{
// create our Node representation
MapNode node = new MapNode(nodeRef);
// set data binding properties
// this will also force initialisation of the props now during the UserTransaction
// it is much better for performance to do this now rather than during page bind
Map<String, Object> props = node.getProperties();
props.put("fullName", ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName")));
NodeRef homeFolderNodeRef = (NodeRef)props.get("homeFolder");
if (homeFolderNodeRef != null)
{
props.put("homeSpace", homeFolderNodeRef);
}
personNodes.add(node);
}
}
// commit the transaction
tx.commit();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_NODEREF), new Object[] {"root"}) );
personNodes = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_GENERIC), err.getMessage()), err );
personNodes = Collections.<Node>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
return personNodes;
}
/**
* Convert a property of unknown type to a String value. A native String value will be
* returned directly, else toString() will be executed, null is returned as null.
*
* @param value Property value
*
* @return value to String or null
*/
public static String safePropertyToString(Serializable value)
{
if (value == null)
{
return null;
}
else if (value instanceof String)
{
return (String)value;
}
else
{
return value.toString();
}
}
/**
* Creates a QName representation for the given String.
* If the String has no namespace the Alfresco namespace is added.
* If the String has a prefix an attempt to resolve the prefix to the
* full URI will be made.
*
* @param str The string to convert
* @return A QName representation of the given string
*/
public static QName resolveToQName(String str)
{
return QName.resolveToQName(getNamespaceService(), str);
}
/**
* Creates a string representation of a QName for the given string.
* If the given string already has a namespace, either a URL or a prefix,
* nothing the given string is returned. If it does not have a namespace
* the Alfresco namespace is added.
*
* @param str The string to convert
* @return A QName String representation of the given string
*/
public static String resolveToQNameString(String str)
{
return QName.resolveToQNameString(getNamespaceService(), str);
}
/**
* Returns an instance of the namespace service
*
* @return The NamespaceService
*/
private static NamespaceService getNamespaceService()
{
if (namespaceService == null)
{
ServiceRegistry svcReg = getServiceRegistry(FacesContext.getCurrentInstance());
namespaceService = svcReg.getNamespaceService();
}
return namespaceService;
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.repository;
import java.util.List;
import javax.faces.context.FacesContext;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.configuration.ConfigurableService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
/**
* Bean that represents the currently logged in user
*
* @author gavinc
*/
public final class User
{
private String homeSpaceId;
private String userName;
private String ticket;
private NodeRef person;
private String fullName = null;
private Boolean administrator = null;
/** cached ref to our user preferences node */
private NodeRef preferencesFolderRef = null;
/**
* Constructor
*
* @param userName constructor for the user
*/
public User(String userName, String ticket, NodeRef person)
{
if (userName == null || ticket == null || person == null)
{
throw new IllegalArgumentException("All user details are mandatory!");
}
this.userName = userName;
this.ticket = ticket;
this.person = person;
}
/**
* @return The user name
*/
public String getUserName()
{
return this.userName;
}
/**
* Return the full name of the Person this User represents
*
* @param service NodeService to use
*
* @return The full name
*/
public String getFullName(NodeService service)
{
if (this.fullName == null)
{
this.fullName = service.getProperty(this.person, ContentModel.PROP_FIRSTNAME) + " " +
service.getProperty(this.person, ContentModel.PROP_LASTNAME);
}
return this.fullName;
}
/**
* @return Retrieves the user's home space (this may be the id of the company home space)
*/
public String getHomeSpaceId()
{
return this.homeSpaceId;
}
/**
* @param homeSpaceId Sets the id of the users home space
*/
public void setHomeSpaceId(String homeSpaceId)
{
this.homeSpaceId = homeSpaceId;
}
/**
* @return Returns the ticket.
*/
public String getTicket()
{
return this.ticket;
}
/**
* @return Returns the person NodeRef
*/
public NodeRef getPerson()
{
return this.person;
}
/**
* @return If the current user has Admin Authority
*/
public boolean isAdmin()
{
if (administrator == null)
{
administrator = Repository.getServiceRegistry(FacesContext.getCurrentInstance())
.getAuthorityService().hasAdminAuthority();
}
return administrator;
}
/**
* Get or create the node used to store user preferences.
* Utilises the 'configurable' aspect on the Person linked to this user.
*/
public synchronized NodeRef getUserPreferencesRef()
{
if (this.preferencesFolderRef == null)
{
FacesContext fc = FacesContext.getCurrentInstance();
ServiceRegistry registry = Repository.getServiceRegistry(fc);
NodeService nodeService = registry.getNodeService();
SearchService searchService = registry.getSearchService();
NamespaceService namespaceService = registry.getNamespaceService();
ConfigurableService configurableService = Repository.getConfigurableService(fc);
NodeRef person = Application.getCurrentUser(fc).getPerson();
if (nodeService.hasAspect(person, ContentModel.ASPECT_CONFIGURABLE) == false)
{
// create the configuration folder for this Person node
configurableService.makeConfigurable(person);
}
// target of the assoc is the configurations folder ref
NodeRef configRef = configurableService.getConfigurationFolder(person);
if (configRef == null)
{
throw new IllegalStateException("Unable to find associated 'configurations' folder for node: " + person);
}
String xpath = NamespaceService.APP_MODEL_PREFIX + ":" + "preferences";
List<NodeRef> nodes = searchService.selectNodes(
configRef,
xpath,
null,
namespaceService,
false);
NodeRef prefRef;
if (nodes.size() == 1)
{
prefRef = nodes.get(0);
}
else
{
// create the preferences Node for this user
ChildAssociationRef childRef = nodeService.createNode(
configRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "preferences"),
ContentModel.TYPE_CMOBJECT);
prefRef = childRef.getChildRef();
}
this.preferencesFolderRef = prefRef;
}
return this.preferencesFolderRef;
}
}

View File

@@ -0,0 +1,633 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.users;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.faces.application.FacesMessage;
import javax.faces.component.UISelectOne;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.alfresco.web.ui.repo.WebResources;
/**
* @author Kevin Roast
*/
public class UserMembersBean
{
private static final String MSG_SUCCESS_INHERIT_NOT = "success_not_inherit_permissions";
private static final String MSG_SUCCESS_INHERIT = "success_inherit_permissions";
private static final String ERROR_DELETE = "error_remove_user";
private static final String OUTCOME_FINISH = "finish";
/** NodeService bean reference */
private NodeService nodeService;
/** SearchService bean reference */
private SearchService searchService;
/** PermissionService bean reference */
private PermissionService permissionService;
/** PersonService bean reference */
private PersonService personService;
/** BrowseBean bean refernce */
private BrowseBean browseBean;
/** OwnableService bean reference */
private OwnableService ownableService;
/** Component reference for Users RichList control */
private UIRichList usersRichList;
/** action context */
private String personAuthority = null;
/** action context */
private String personName = null;
/** datamodel for table of roles for current person */
private DataModel personRolesDataModel = null;
/** roles for current person */
private List<PermissionWrapper> personRoles = null;
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param searchService The search service
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param permissionService The PermissionService to set.
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* @param ownableService The ownableService to set.
*/
public void setOwnableService(OwnableService ownableService)
{
this.ownableService = ownableService;
}
/**
* @param personService The personService to set.
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* @return The space to work against
*/
public Node getSpace()
{
return this.browseBean.getActionSpace();
}
/**
* @return Returns the usersRichList.
*/
public UIRichList getUsersRichList()
{
return this.usersRichList;
}
/**
* @param usersRichList The usersRichList to set.
*/
public void setUsersRichList(UIRichList usersRichList)
{
this.usersRichList = usersRichList;
// force refresh on exit of the page (as this property is set by JSF on view restore)
this.usersRichList.setValue(null);
}
/**
* Returns the properties for current Person roles JSF DataModel
*
* @return JSF DataModel representing the current Person roles
*/
public DataModel getPersonRolesDataModel()
{
if (this.personRolesDataModel == null)
{
this.personRolesDataModel = new ListDataModel();
}
this.personRolesDataModel.setWrappedData(this.personRoles);
return this.personRolesDataModel;
}
/**
* @return Returns the current person authority.
*/
public String getPersonAuthority()
{
return this.personAuthority;
}
/**
* @param person The person person authority to set.
*/
public void setPersonAuthority(String person)
{
this.personAuthority = person;
}
/**
* @return Returns the personName.
*/
public String getPersonName()
{
return this.personName;
}
/**
* @param personName The personName to set.
*/
public void setPersonName(String personName)
{
this.personName = personName;
}
/**
* @return true if the current user can change permissions on this Space
*/
public boolean getHasChangePermissions()
{
return getSpace().hasPermission(PermissionService.CHANGE_PERMISSIONS);
}
/**
* @return Returns the inherit parent permissions flag set for the current space.
*/
public boolean isInheritPermissions()
{
return this.permissionService.getInheritParentPermissions(getSpace().getNodeRef());
}
/**
* @param inheritPermissions The inheritPermissions to set.
*/
public void setInheritPermissions(boolean inheritPermissions)
{
// stub - no impl as changes are made immediately using a ValueChanged listener
}
/**
* Return the owner username
*/
public String getOwner()
{
return this.ownableService.getOwner(getSpace().getNodeRef());
}
/**
* @return the list of user nodes for list data binding
*/
public List<Map> getUsers()
{
FacesContext context = FacesContext.getCurrentInstance();
List<Map> personNodes = null;
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
// Return all the permissions set against the current node
// for any authentication instance (user).
// Then combine them into a single list for each authentication found.
User user = Application.getCurrentUser(context);
Map<String, List<String>> permissionMap = new HashMap<String, List<String>>(13, 1.0f);
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(getSpace().getNodeRef());
if (permissions != null)
{
for (AccessPermission permission : permissions)
{
// we are only interested in Allow and not groups/owner etc.
if (permission.getAccessStatus() == AccessStatus.ALLOWED &&
(permission.getAuthorityType() == AuthorityType.USER ||
permission.getAuthorityType() == AuthorityType.GROUP ||
permission.getAuthorityType() == AuthorityType.EVERYONE))
{
String authority = permission.getAuthority();
List<String> userPermissions = permissionMap.get(authority);
if (userPermissions == null)
{
// create for first time
userPermissions = new ArrayList<String>(4);
permissionMap.put(authority, userPermissions);
}
// add the permission name for this authority
userPermissions.add(permission.getPermission());
}
}
}
// for each authentication (username key) found we get the Person
// node represented by it and use that for our list databinding object
personNodes = new ArrayList<Map>(permissionMap.size());
for (String authority : permissionMap.keySet())
{
// check if we are dealing with a person (User Authority)
if (personService.personExists(authority))
{
NodeRef nodeRef = personService.getPerson(authority);
if (nodeRef != null)
{
// create our Node representation
MapNode node = new MapNode(nodeRef);
// set data binding properties
// this will also force initialisation of the props now during the UserTransaction
// it is much better for performance to do this now rather than during page bind
Map<String, Object> props = node.getProperties();
props.put("fullName", ((String)props.get("firstName")) + ' ' + ((String)props.get("lastName")));
String userName = (String)props.get("userName");
props.put("roles", listToString(context, permissionMap.get(authority)));
props.put("icon", WebResources.IMAGE_PERSON);
personNodes.add(node);
}
}
else
{
// need a map (dummy node) to represent props for this Group Authority
Map<String, Object> node = new HashMap<String, Object>(5, 1.0f);
node.put("fullName", authority.substring(PermissionService.GROUP_PREFIX.length()));
node.put("userName", authority);
node.put("id", authority);
node.put("roles", listToString(context, permissionMap.get(authority)));
node.put("icon", WebResources.IMAGE_GROUP);
personNodes.add(node);
}
}
// commit the transaction
tx.commit();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_NODEREF), new Object[] {"root"}) );
personNodes = Collections.<Map>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_GENERIC), err.getMessage()), err );
personNodes = Collections.<Map>emptyList();
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
}
return personNodes;
}
private static String listToString(FacesContext context, List<String> list)
{
StringBuilder buf = new StringBuilder();
if (list != null)
{
for (int i=0; i<list.size(); i++)
{
if (buf.length() != 0)
{
buf.append(", ");
}
buf.append(Application.getMessage(context, list.get(i)));
}
}
return buf.toString();
}
// ------------------------------------------------------------------------------
// Action event handlers
/**
* Action event called by all actions that need to setup a Person context on
* the UserMembers bean before an action page is called. The context will be a
* Authority in setPersonAuthority() which can be retrieved on the action page from
* UserMembersBean.setPersonAuthority().
*/
public void setupUserAction(ActionEvent event)
{
FacesContext context = FacesContext.getCurrentInstance();
UIActionLink link = (UIActionLink) event.getComponent();
Map<String, String> params = link.getParameterMap();
String authority = params.get("userName");
if (authority != null && authority.length() != 0)
{
try
{
if (this.personService.personExists(authority))
{
// create the node ref, then our node representation
NodeRef ref = personService.getPerson(authority);
Node node = new Node(ref);
// setup convience function for current user full name
setPersonName((String)node.getProperties().get(ContentModel.PROP_FIRSTNAME) + ' ' +
(String)node.getProperties().get(ContentModel.PROP_LASTNAME));
}
else
{
setPersonName(authority);
}
// setup roles for this Authority
List<PermissionWrapper> userPermissions = new ArrayList<PermissionWrapper>(4);
Set<AccessPermission> permissions = permissionService.getAllSetPermissions(getSpace().getNodeRef());
if (permissions != null)
{
for (AccessPermission permission : permissions)
{
// we are only interested in Allow permissions
if (permission.getAccessStatus() == AccessStatus.ALLOWED)
{
if (authority.equals(permission.getAuthority()))
{
// found a permission for this user authentiaction
PermissionWrapper wrapper = new PermissionWrapper(
permission.getPermission(),
Application.getMessage(context, permission.getPermission()));
userPermissions.add(wrapper);
}
}
}
}
// action context setup
this.personRoles = userPermissions;
setPersonAuthority(authority);
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext
.getCurrentInstance(), Repository.ERROR_GENERIC), new Object[] { err.getMessage() }));
}
}
else
{
setPersonAuthority(null);
}
}
/**
* Inherit parent Space permissions value changed by the user
*/
public void inheritPermissionsValueChanged(ValueChangeEvent event)
{
try
{
// change the value to the new selected value
boolean inheritPermissions = (Boolean)event.getNewValue();
this.permissionService.setInheritParentPermissions(getSpace().getNodeRef(), inheritPermissions);
// inform the user that the change occured
FacesContext context = FacesContext.getCurrentInstance();
String msg;
if (inheritPermissions)
{
msg = Application.getMessage(context, MSG_SUCCESS_INHERIT);
}
else
{
msg = Application.getMessage(context, MSG_SUCCESS_INHERIT_NOT);
}
FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg);
context.addMessage(event.getComponent().getClientId(context), facesMsg);
}
catch (Throwable e)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
}
}
/**
* Action handler called when the Add Role button is pressed to process the current selection
*/
public void addRole(ActionEvent event)
{
UISelectOne rolePicker = (UISelectOne)event.getComponent().findComponent("roles");
String role = (String)rolePicker.getValue();
if (role != null)
{
FacesContext context = FacesContext.getCurrentInstance();
PermissionWrapper wrapper = new PermissionWrapper(role, Application.getMessage(context, role));
this.personRoles.add(wrapper);
}
}
/**
* Action handler called when the Remove button is pressed to remove a role from current user
*/
public void removeRole(ActionEvent event)
{
PermissionWrapper wrapper = (PermissionWrapper)this.personRolesDataModel.getRowData();
if (wrapper != null)
{
this.personRoles.remove(wrapper);
}
}
/**
* Action handler called when the Finish button is clicked on the Edit User Roles page
*/
public String finishOK()
{
String outcome = OUTCOME_FINISH;
FacesContext context = FacesContext.getCurrentInstance();
// persist new user permissions
if (this.personRoles != null && getPersonAuthority() != null)
{
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context);
tx.begin();
// clear the currently set permissions for this user
// and add each of the new permissions in turn
NodeRef nodeRef = getSpace().getNodeRef();
this.permissionService.clearPermission(nodeRef, getPersonAuthority());
for (PermissionWrapper wrapper : personRoles)
{
this.permissionService.setPermission(
nodeRef,
getPersonAuthority(),
wrapper.getPermission(),
true);
}
tx.commit();
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
context, Repository.ERROR_GENERIC), err.getMessage()), err );
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
outcome = null;
}
}
return outcome;
}
/**
* Action handler called when the OK button is clicked on the Remove User page
*/
public String removeOK()
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// remove the invited User
if (getPersonAuthority() != null)
{
// clear permissions for the specified Authority
this.permissionService.clearPermission(getSpace().getNodeRef(), getPersonAuthority());
}
// commit the transaction
tx.commit();
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext
.getCurrentInstance(), ERROR_DELETE), e.getMessage()), e);
}
return OUTCOME_FINISH;
}
// ------------------------------------------------------------------------------
// Inner classes
/**
* Wrapper class for list data model to display current roles for user
*/
public static class PermissionWrapper
{
public PermissionWrapper(String permission, String label)
{
this.permission = permission;
this.label = label;
}
public String getRole()
{
return this.label;
}
public String getPermission()
{
return this.permission;
}
private String label;
private String permission;
}
}

View File

@@ -0,0 +1,311 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.users;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.IContextListener;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.LoginBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.alfresco.web.ui.common.component.data.UIRichList;
import org.apache.log4j.Logger;
/**
* @author Kevin Roast
*/
public class UsersBean implements IContextListener
{
private static Logger logger = Logger.getLogger(UsersBean.class);
public static final String ERROR_PASSWORD_MATCH = "error_password_match";
private static final String ERROR_DELETE = "error_delete_user";
private static final String DEFAULT_OUTCOME = "manageUsers";
/** NodeService bean reference */
private NodeService nodeService;
/** SearchService bean reference */
private SearchService searchService;
/** AuthenticationService bean reference */
private AuthenticationService authenticationService;
/** Component reference for Users RichList control */
private UIRichList usersRichList;
/** action context */
private Node person = null;
private String password = null;
private String confirm = null;
// ------------------------------------------------------------------------------
// Construction
/**
* Default Constructor
*/
public UsersBean()
{
UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this);
}
// ------------------------------------------------------------------------------
// Bean property getters and setters
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param searchService the search service
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @param authenticationService The AuthenticationService to set.
*/
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
/**
* @return Returns the usersRichList.
*/
public UIRichList getUsersRichList()
{
return this.usersRichList;
}
/**
* @param usersRichList The usersRichList to set.
*/
public void setUsersRichList(UIRichList usersRichList)
{
this.usersRichList = usersRichList;
}
/**
* @return the list of user Nodes to display
*/
public List<Node> getUsers()
{
return Repository.getUsers(FacesContext.getCurrentInstance(), this.nodeService,
this.searchService);
}
/**
* @return Returns the confirm password.
*/
public String getConfirm()
{
return this.confirm;
}
/**
* @param confirm The confirm password to set.
*/
public void setConfirm(String confirm)
{
this.confirm = confirm;
}
/**
* @return Returns the password.
*/
public String getPassword()
{
return this.password;
}
/**
* @param password The password to set.
*/
public void setPassword(String password)
{
this.password = password;
}
/**
* @return Returns the person context.
*/
public Node getPerson()
{
return this.person;
}
/**
* @param person The person context to set.
*/
public void setPerson(Node person)
{
this.person = person;
}
/**
* Action event called by all actions that need to setup a Person context on
* the Users bean before an action page is called. The context will be a
* Person Node in setPerson() which can be retrieved on the action page from
* UsersBean.getPerson().
*/
public void setupUserAction(ActionEvent event)
{
UIActionLink link = (UIActionLink) event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
if (logger.isDebugEnabled())
logger.debug("Setup for action, setting current Person to: " + id);
try
{
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
Node node = new Node(ref);
// remember the Person node
setPerson(node);
// clear the UI state in preparation for finishing the action
// and returning to the main page
contextUpdated();
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext
.getCurrentInstance(), Repository.ERROR_NODEREF), new Object[] { id }));
}
}
else
{
setPerson(null);
}
}
/**
* Action handler called when the OK button is clicked on the Delete User page
*/
public String deleteOK()
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
// we only delete the user auth if Alfresco is managing the authentication
Map session = context.getExternalContext().getSessionMap();
if (session.get(LoginBean.LOGIN_EXTERNAL_AUTH) == null)
{
// delete the User authentication
authenticationService.deleteAuthentication((String) getPerson().getProperties().get("userName"));
}
// delete the associated Person
this.nodeService.deleteNode(getPerson().getNodeRef());
// commit the transaction
tx.commit();
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext
.getCurrentInstance(), ERROR_DELETE), e.getMessage()), e);
}
return DEFAULT_OUTCOME;
}
/**
* Action handler called for OK button press on Change Password screen
*/
public String changePasswordOK()
{
String outcome = DEFAULT_OUTCOME;
if (this.password != null && this.confirm != null && this.password.equals(this.confirm))
{
try
{
String userName = (String)this.person.getProperties().get(ContentModel.PROP_USERNAME);
this.authenticationService.setAuthentication(userName, this.password.toCharArray());
}
catch (Exception e)
{
outcome = null;
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext
.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
}
}
else
{
outcome = null;
Utils.addErrorMessage(Application.getMessage(FacesContext.getCurrentInstance(),
ERROR_PASSWORD_MATCH));
}
return outcome;
}
// ------------------------------------------------------------------------------
// IContextListener implementation
/**
* @see org.alfresco.web.app.context.IContextListener#contextUpdated()
*/
public void contextUpdated()
{
if (this.usersRichList != null)
{
this.usersRichList.setValue(null);
}
}
}

View File

@@ -0,0 +1,314 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.BrowseBean;
import org.alfresco.web.bean.NavigationBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstract bean used as the base class for all wizard backing beans.
*
* @author gavinc
*/
public abstract class AbstractWizardBean
{
private static Log logger = LogFactory.getLog(AbstractWizardBean.class);
/** I18N messages */
private static final String MSG_NOT_SET = "value_not_set";
protected static final String FINISH_OUTCOME = "finish";
protected static final String CANCEL_OUTCOME = "cancel";
protected static final String DEFAULT_INSTRUCTION_ID = "default_instruction";
protected static final String SUMMARY_TITLE_ID = "summary";
protected static final String SUMMARY_DESCRIPTION_ID = "summary_desc";
// common wizard properties
protected int currentStep = 1;
protected boolean editMode = false;
protected NodeService nodeService;
protected FileFolderService fileFolderService;
protected SearchService searchService;
protected NavigationBean navigator;
protected BrowseBean browseBean;
/**
* @return Returns the wizard description
*/
public abstract String getWizardDescription();
/**
* @return Returns the wizard title
*/
public abstract String getWizardTitle();
/**
* @return Returns the title for the current step
*/
public abstract String getStepTitle();
/**
* @return Returns the description for the current step
*/
public abstract String getStepDescription();
/**
* @return Returns the instructional text for the current step
*/
public abstract String getStepInstructions();
/**
* Determines the outcome string for the given step number
*
* @param step The step number to get the outcome for
* @return The outcome
*/
protected abstract String determineOutcomeForStep(int step);
/**
* Handles the finish button being pressed
*
* @return The finish outcome
*/
public abstract String finish();
/**
* Action listener called when the wizard is being launched allowing
* state to be setup
*/
public void startWizard(ActionEvent event)
{
// refresh the UI, calling this method now is fine as it basically makes sure certain
// beans clear the state - so when we finish the wizard other beans will have been reset
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
// make sure the wizard is not in edit mode
this.editMode = false;
// initialise the wizard in case we are launching
// after it was navigated away from
init();
if (logger.isDebugEnabled())
logger.debug("Started wizard : " + getWizardTitle());
}
/**
* Action listener called when the wizard is being launched for
* editing an existing node.
*/
public void startWizardForEdit(ActionEvent event)
{
// refresh the UI, calling this method now is fine as it basically makes sure certain
// beans clear the state - so when we finish the wizard other beans will have been reset
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
// set the wizard in edit mode
this.editMode = true;
// populate the wizard's default values with the current value
// from the node being edited
init();
populate();
if (logger.isDebugEnabled())
logger.debug("Started wizard : " + getWizardTitle() + " for editing");
}
/**
* Deals with the next button being pressed
*
* @return
*/
public String next()
{
this.currentStep++;
// determine which page to go to next
String outcome = determineOutcomeForStep(this.currentStep);
if (logger.isDebugEnabled())
{
logger.debug("current step is now: " + this.currentStep);
logger.debug("Next outcome: " + outcome);
}
// return the outcome for navigation
return outcome;
}
/**
* Deals with the back button being pressed
*
* @return
*/
public String back()
{
this.currentStep--;
// determine which page to go to next
String outcome = determineOutcomeForStep(this.currentStep);
if (logger.isDebugEnabled())
{
logger.debug("current step is now: " + this.currentStep);
logger.debug("Back outcome: " + outcome);
}
// return the outcome for navigation
return outcome;
}
/**
* Handles the cancelling of the wizard
*
* @return The cancel outcome
*/
public String cancel()
{
// reset the state
init();
return CANCEL_OUTCOME;
}
/**
* Initialises the wizard
*/
public void init()
{
this.currentStep = 1;
}
/**
* Populates the wizard's values with the current values
* of the node about to be edited
*/
public void populate()
{
// subclasses will override this method to setup accordingly
}
/**
* @return Returns the nodeService.
*/
public NodeService getNodeService()
{
return this.nodeService;
}
/**
* @param nodeService The nodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param fileFolderService used to manipulate folder/folder model nodes
*/
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
/**
* @param searchService the service used to find nodes
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @return Returns the navigation bean instance.
*/
public NavigationBean getNavigator()
{
return navigator;
}
/**
* @param navigator The NavigationBean to set.
*/
public void setNavigator(NavigationBean navigator)
{
this.navigator = navigator;
}
/**
* @return The BrowseBean
*/
public BrowseBean getBrowseBean()
{
return this.browseBean;
}
/**
* @param browseBean The BrowseBean to set.
*/
public void setBrowseBean(BrowseBean browseBean)
{
this.browseBean = browseBean;
}
/**
* Build summary table from the specified list of Labels and Values
*
* @param labels Array of labels to display
* @param values Array of values to display
*
* @return summary table HTML
*/
protected String buildSummary(String[] labels, String[] values)
{
if (labels == null || values == null || labels.length != values.length)
{
throw new IllegalArgumentException("Labels and Values passed to summary must be valid and of equal length.");
}
String msg = Application.getMessage(FacesContext.getCurrentInstance(), MSG_NOT_SET);
String notSetMsg = "&lt;" + msg + "&gt;";
StringBuilder buf = new StringBuilder(256);
buf.append("<table cellspacing='4' cellpadding='2' border='0' class='summary'>");
for (int i=0; i<labels.length; i++)
{
String value = values[i];
buf.append("<tr><td valign='top'><b>");
buf.append(labels[i]);
buf.append(":</b></td><td>");
buf.append(value != null ? value : notSetMsg);
buf.append("</td></tr>");
}
buf.append("</table>");
return buf.toString();
}
}

View File

@@ -0,0 +1,320 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.io.File;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import javax.faces.context.FacesContext;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.FileUploadBean;
import org.alfresco.web.bean.repository.Repository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Handler class used by the Add Content Wizard
*
* @author gavinc
*/
public class AddContentWizard extends BaseContentWizard
{
private static Log logger = LogFactory.getLog(AddContentWizard.class);
// TODO: retrieve these from the config service
private static final String WIZARD_TITLE_ID = "add_content_title";
private static final String WIZARD_DESC_ID = "add_content_desc";
private static final String STEP1_TITLE_ID = "add_conent_step1_title";
private static final String STEP1_DESCRIPTION_ID = "add_conent_step1_desc";
private static final String STEP2_TITLE_ID = "add_conent_step2_title";
private static final String STEP2_DESCRIPTION_ID = "add_conent_step2_desc";
// add content wizard specific properties
private File file;
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#next()
*/
public String next()
{
String outcome = super.next();
// if the outcome is "properties" we pre-set the content type and other
// fields accordingly
if (outcome.equals("properties"))
{
this.contentType = Repository.getMimeTypeForFileName(
FacesContext.getCurrentInstance(), this.fileName);
// set default for in-line editing flag
this.inlineEdit = (this.contentType.equals(MimetypeMap.MIMETYPE_HTML));
// Try and extract metadata from the file
ContentReader cr = new FileContentReader(this.file);
cr.setMimetype(this.contentType);
// create properties for content type
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>(5, 1.0f);
if (Repository.extractMetadata(FacesContext.getCurrentInstance(), cr, contentProps))
{
this.author = (String)(contentProps.get(ContentModel.PROP_CREATOR));
this.title = (String)(contentProps.get(ContentModel.PROP_TITLE));
this.description = (String)(contentProps.get(ContentModel.PROP_DESCRIPTION));
}
if (this.title == null)
{
this.title = this.fileName;
}
}
return outcome;
}
/**
* Deals with the finish button being pressed
*
* @return outcome
*/
public String finish()
{
String outcome = saveContent(this.file, null);
// now we know the new details are in the repository, reset the
// client side node representation so the new details are retrieved
if (this.editMode)
{
this.browseBean.getDocument().reset();
}
return outcome;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardDescription()
*/
public String getWizardDescription()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_DESC_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardTitle()
*/
public String getWizardTitle()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_TITLE_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription()
*/
public String getStepDescription()
{
String stepDesc = null;
switch (this.currentStep)
{
case 1:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_DESCRIPTION_ID);
break;
}
case 2:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_DESCRIPTION_ID);
break;
}
case 3:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), SUMMARY_DESCRIPTION_ID);
break;
}
default:
{
stepDesc = "";
}
}
return stepDesc;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepTitle()
*/
public String getStepTitle()
{
String stepTitle = null;
switch (this.currentStep)
{
case 1:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_TITLE_ID);
break;
}
case 2:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_TITLE_ID);
break;
}
case 3:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), SUMMARY_TITLE_ID);
break;
}
default:
{
stepTitle = "";
}
}
return stepTitle;
}
/**
* Initialises the wizard
*/
public void init()
{
super.init();
clearUpload();
this.file = null;
}
/**
* @return Returns the message to display when a file has been uploaded
*/
public String getFileUploadSuccessMsg()
{
String msg = Application.getMessage(FacesContext.getCurrentInstance(), "file_upload_success");
return MessageFormat.format(msg, new Object[] {getFileName()});
}
/**
* @return Returns the name of the file
*/
public String getFileName()
{
// try and retrieve the file and filename from the file upload bean
// representing the file we previously uploaded.
FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
this.file = fileBean.getFile();
this.fileName = fileBean.getFileName();
}
return this.fileName;
}
/**
* @param fileName The name of the file
*/
public void setFileName(String fileName)
{
this.fileName = fileName;
// we also need to keep the file upload bean in sync
FacesContext ctx = FacesContext.getCurrentInstance();
FileUploadBean fileBean = (FileUploadBean)ctx.getExternalContext().getSessionMap().
get(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
if (fileBean != null)
{
fileBean.setFileName(this.fileName);
}
}
/**
* @return Returns the summary data for the wizard.
*/
public String getSummary()
{
ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
return buildSummary(
new String[] {bundle.getString("file_name"), bundle.getString("type"),
bundle.getString("content_type"), bundle.getString("title"),
bundle.getString("description"), bundle.getString("author"),
bundle.getString("inline_editable")},
new String[] {this.fileName, getSummaryObjectType(), getSummaryContentType(), this.title,
this.description, this.author, Boolean.toString(this.inlineEdit)});
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#determineOutcomeForStep(int)
*/
protected String determineOutcomeForStep(int step)
{
String outcome = null;
switch(step)
{
case 1:
{
outcome = "upload";
break;
}
case 2:
{
outcome = "properties";
break;
}
case 3:
{
outcome = "summary";
break;
}
default:
{
outcome = CANCEL_OUTCOME;
}
}
return outcome;
}
/**
* Deletes the uploaded file and removes the FileUploadBean from the session
*/
private void clearUpload()
{
// delete the temporary file we uploaded earlier
if (this.file != null)
{
this.file.delete();
}
// remove the file upload bean from the session
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.getExternalContext().getSessionMap().remove(FileUploadBean.FILE_UPLOAD_BEAN_NAME);
}
}

View File

@@ -0,0 +1,921 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.AddFeaturesActionExecuter;
import org.alfresco.repo.action.executer.CheckInActionExecuter;
import org.alfresco.repo.action.executer.CheckOutActionExecuter;
import org.alfresco.repo.action.executer.CopyActionExecuter;
import org.alfresco.repo.action.executer.ImageTransformActionExecuter;
import org.alfresco.repo.action.executer.ImporterActionExecuter;
import org.alfresco.repo.action.executer.LinkCategoryActionExecuter;
import org.alfresco.repo.action.executer.MailActionExecuter;
import org.alfresco.repo.action.executer.MoveActionExecuter;
import org.alfresco.repo.action.executer.SimpleWorkflowActionExecuter;
import org.alfresco.repo.action.executer.SpecialiseTypeActionExecuter;
import org.alfresco.repo.action.executer.TransformActionExecuter;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Base handler class containing common code used by the New Space Wizard and New Action Wizard
*
* @author gavinc kevinr
*/
public abstract class BaseActionWizard extends AbstractWizardBean
{
// parameter names for actions
public static final String PROP_CATEGORY = "category";
public static final String PROP_ASPECT = "aspect";
public static final String PROP_DESTINATION = "destinationLocation";
public static final String PROP_APPROVE_STEP_NAME = "approveStepName";
public static final String PROP_APPROVE_ACTION = "approveAction";
public static final String PROP_APPROVE_FOLDER = "approveFolder";
public static final String PROP_REJECT_STEP_PRESENT = "rejectStepPresent";
public static final String PROP_REJECT_STEP_NAME = "rejectStepName";
public static final String PROP_REJECT_ACTION = "rejectAction";
public static final String PROP_REJECT_FOLDER = "rejectFolder";
public static final String PROP_CHECKIN_DESC = "checkinDescription";
public static final String PROP_CHECKIN_MINOR = "checkinMinorChange";
public static final String PROP_TRANSFORMER = "transformer";
public static final String PROP_IMAGE_TRANSFORMER = "imageTransformer";
public static final String PROP_TRANSFORM_OPTIONS = "transformOptions";
public static final String PROP_ENCODING = "encoding";
public static final String PROP_MESSAGE = "message";
public static final String PROP_SUBJECT = "subject";
public static final String PROP_TO = "to";
public static final String PROP_OBJECT_TYPE = "objecttype";
private static final Log logger = LogFactory.getLog(BaseActionWizard.class);
private static final String IMPORT_ENCODING = "UTF-8";
// new rule/action wizard specific properties
protected boolean multiActionMode = false;
protected String action;
protected ActionService actionService;
protected DictionaryService dictionaryService;
protected MimetypeService mimetypeService;
protected List<SelectItem> actions;
protected List<SelectItem> transformers;
protected List<SelectItem> imageTransformers;
protected List<SelectItem> aspects;
protected List<SelectItem> users;
protected List<SelectItem> encodings;
protected Map<String, String> actionDescriptions;
protected Map<String, Serializable> currentActionProperties;
protected List<SelectItem> objectTypes;
/**
* Initialises the wizard
*/
public void init()
{
super.init();
this.action = "add-features";
this.users = null;
this.actions = null;
this.actionDescriptions = null;
this.currentActionProperties = new HashMap<String, Serializable>(3);
// default the approve and reject actions
this.currentActionProperties.put(PROP_APPROVE_ACTION, "move");
this.currentActionProperties.put(PROP_REJECT_STEP_PRESENT, "yes");
this.currentActionProperties.put(PROP_REJECT_ACTION, "move");
// default the checkin minor change
this.currentActionProperties.put(PROP_CHECKIN_MINOR, new Boolean(true));
}
/**
* Build the param map for the current Action instance
*
* @return param map
*/
protected Map<String, Serializable> buildActionParams()
{
// set up parameters maps for the action
Map<String, Serializable> actionParams = new HashMap<String, Serializable>();
if (this.action.equals(AddFeaturesActionExecuter.NAME))
{
QName aspect = Repository.resolveToQName((String)this.currentActionProperties.get(PROP_ASPECT));
actionParams.put(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, aspect);
}
else if (this.action.equals(CopyActionExecuter.NAME))
{
// add the destination space id to the action properties
NodeRef destNodeRef = (NodeRef)this.currentActionProperties.get(PROP_DESTINATION);
actionParams.put(CopyActionExecuter.PARAM_DESTINATION_FOLDER, destNodeRef);
// add the type and name of the association to create when the copy
// is performed
actionParams.put(CopyActionExecuter.PARAM_ASSOC_TYPE_QNAME,
ContentModel.ASSOC_CONTAINS);
actionParams.put(CopyActionExecuter.PARAM_ASSOC_QNAME,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copy"));
}
else if (this.action.equals(MoveActionExecuter.NAME))
{
// add the destination space id to the action properties
NodeRef destNodeRef = (NodeRef)this.currentActionProperties.get(PROP_DESTINATION);
actionParams.put(MoveActionExecuter.PARAM_DESTINATION_FOLDER, destNodeRef);
// add the type and name of the association to create when the move
// is performed
actionParams.put(MoveActionExecuter.PARAM_ASSOC_TYPE_QNAME,
ContentModel.ASSOC_CONTAINS);
actionParams.put(MoveActionExecuter.PARAM_ASSOC_QNAME,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "move"));
}
else if (this.action.equals(SimpleWorkflowActionExecuter.NAME))
{
// add the approve step name
actionParams.put(SimpleWorkflowActionExecuter.PARAM_APPROVE_STEP,
(String)this.currentActionProperties.get(PROP_APPROVE_STEP_NAME));
// add whether the approve step will copy or move the content
boolean approveMove = true;
String approveAction = (String)this.currentActionProperties.get(PROP_APPROVE_ACTION);
if (approveAction != null && approveAction.equals("copy"))
{
approveMove = false;
}
actionParams.put(SimpleWorkflowActionExecuter.PARAM_APPROVE_MOVE, Boolean.valueOf(approveMove));
// add the destination folder of the content
NodeRef approveDestNodeRef = null;
Object approveDestNode = this.currentActionProperties.get(PROP_APPROVE_FOLDER);
if (approveDestNode instanceof NodeRef)
{
approveDestNodeRef = (NodeRef)approveDestNode;
}
else if (approveDestNode instanceof String)
{
approveDestNodeRef = new NodeRef((String)approveDestNode);
}
actionParams.put(SimpleWorkflowActionExecuter.PARAM_APPROVE_FOLDER, approveDestNodeRef);
// determine whether we have a reject step or not
boolean requireReject = true;
String rejectStepPresent = (String)this.currentActionProperties.get(PROP_REJECT_STEP_PRESENT);
if (rejectStepPresent != null && rejectStepPresent.equals("no"))
{
requireReject = false;
}
if (requireReject)
{
// add the reject step name
actionParams.put(SimpleWorkflowActionExecuter.PARAM_REJECT_STEP,
(String)this.currentActionProperties.get(PROP_REJECT_STEP_NAME));
// add whether the reject step will copy or move the content
boolean rejectMove = true;
String rejectAction = (String)this.currentActionProperties.get(PROP_REJECT_ACTION);
if (rejectAction != null && rejectAction.equals("copy"))
{
rejectMove = false;
}
actionParams.put(SimpleWorkflowActionExecuter.PARAM_REJECT_MOVE, Boolean.valueOf(rejectMove));
// add the destination folder of the content
NodeRef rejectDestNodeRef = null;
Object rejectDestNode = this.currentActionProperties.get(PROP_REJECT_FOLDER);
if (rejectDestNode instanceof NodeRef)
{
rejectDestNodeRef = (NodeRef)rejectDestNode;
}
else if (rejectDestNode instanceof String)
{
rejectDestNodeRef = new NodeRef((String)rejectDestNode);
}
actionParams.put(SimpleWorkflowActionExecuter.PARAM_REJECT_FOLDER, rejectDestNodeRef);
}
}
else if (this.action.equals(LinkCategoryActionExecuter.NAME))
{
// add the classifiable aspect
actionParams.put(LinkCategoryActionExecuter.PARAM_CATEGORY_ASPECT,
ContentModel.ASPECT_GEN_CLASSIFIABLE);
// put the selected category in the action params
NodeRef catNodeRef = (NodeRef)this.currentActionProperties.get(PROP_CATEGORY);
actionParams.put(LinkCategoryActionExecuter.PARAM_CATEGORY_VALUE,
catNodeRef);
}
else if (this.action.equals(CheckOutActionExecuter.NAME))
{
// specify the location the checked out working copy should go
// add the destination space id to the action properties
NodeRef destNodeRef = (NodeRef)this.currentActionProperties.get(PROP_DESTINATION);
actionParams.put(CheckOutActionExecuter.PARAM_DESTINATION_FOLDER, destNodeRef);
// add the type and name of the association to create when the
// check out is performed
actionParams.put(CheckOutActionExecuter.PARAM_ASSOC_TYPE_QNAME,
ContentModel.ASSOC_CONTAINS);
actionParams.put(CheckOutActionExecuter.PARAM_ASSOC_QNAME,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "checkout"));
}
else if (this.action.equals(CheckInActionExecuter.NAME))
{
// add the description for the checkin to the action params
actionParams.put(CheckInActionExecuter.PARAM_DESCRIPTION,
this.currentActionProperties.get(PROP_CHECKIN_DESC));
// add the minor change flag
actionParams.put(CheckInActionExecuter.PARAM_MINOR_CHANGE,
this.currentActionProperties.get(PROP_CHECKIN_MINOR));
}
else if (this.action.equals(TransformActionExecuter.NAME))
{
// add the transformer to use
actionParams.put(TransformActionExecuter.PARAM_MIME_TYPE,
this.currentActionProperties.get(PROP_TRANSFORMER));
// add the destination space id to the action properties
NodeRef destNodeRef = (NodeRef)this.currentActionProperties.get(PROP_DESTINATION);
actionParams.put(TransformActionExecuter.PARAM_DESTINATION_FOLDER, destNodeRef);
// add the type and name of the association to create when the copy
// is performed
actionParams.put(TransformActionExecuter.PARAM_ASSOC_TYPE_QNAME,
ContentModel.ASSOC_CONTAINS);
actionParams.put(TransformActionExecuter.PARAM_ASSOC_QNAME,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copy"));
}
else if (this.action.equals(ImageTransformActionExecuter.NAME))
{
// add the transformer to use
actionParams.put(ImageTransformActionExecuter.PARAM_MIME_TYPE,
this.currentActionProperties.get(PROP_IMAGE_TRANSFORMER));
// add the options
actionParams.put(ImageTransformActionExecuter.PARAM_CONVERT_COMMAND,
this.currentActionProperties.get(PROP_TRANSFORM_OPTIONS));
// add the destination space id to the action properties
NodeRef destNodeRef = (NodeRef)this.currentActionProperties.get(PROP_DESTINATION);
actionParams.put(TransformActionExecuter.PARAM_DESTINATION_FOLDER, destNodeRef);
// add the type and name of the association to create when the copy
// is performed
actionParams.put(TransformActionExecuter.PARAM_ASSOC_TYPE_QNAME,
ContentModel.ASSOC_CONTAINS);
actionParams.put(TransformActionExecuter.PARAM_ASSOC_QNAME,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copy"));
}
else if (this.action.equals(MailActionExecuter.NAME))
{
// add the actual email text to send
actionParams.put(MailActionExecuter.PARAM_TEXT,
this.currentActionProperties.get(PROP_MESSAGE));
// add the person it's going to
actionParams.put(MailActionExecuter.PARAM_TO,
this.currentActionProperties.get(PROP_TO));
// add the subject for the email
actionParams.put(MailActionExecuter.PARAM_SUBJECT,
this.currentActionProperties.get(PROP_SUBJECT));
}
else if (this.action.equals(ImporterActionExecuter.NAME))
{
// add the encoding
actionParams.put(ImporterActionExecuter.PARAM_ENCODING, IMPORT_ENCODING);
// add the destination for the import
NodeRef destNodeRef = (NodeRef)this.currentActionProperties.get(PROP_DESTINATION);
actionParams.put(ImporterActionExecuter.PARAM_DESTINATION_FOLDER, destNodeRef);
}
else if (this.action.equals(SpecialiseTypeActionExecuter.NAME) == true)
{
// add the specialisation type
String objectType = (String)this.currentActionProperties.get(PROP_OBJECT_TYPE);
actionParams.put(SpecialiseTypeActionExecuter.PARAM_TYPE_NAME, QName.createQName(objectType));
}
return actionParams;
}
/**
* Populate the actionProperties member variable with correct props for the current action
* using the supplied property map.
*
* @param actionProps Map to retrieve props appropriate to the current action from
*/
protected void populateActionFromProperties(Map<String, Serializable> actionProps)
{
if (this.action.equals(AddFeaturesActionExecuter.NAME))
{
QName aspect = (QName)actionProps.get(AddFeaturesActionExecuter.PARAM_ASPECT_NAME);
this.currentActionProperties.put(PROP_ASPECT, aspect.toString());
}
else if (this.action.equals(CopyActionExecuter.NAME))
{
NodeRef destNodeRef = (NodeRef)actionProps.get(CopyActionExecuter.PARAM_DESTINATION_FOLDER);
this.currentActionProperties.put(PROP_DESTINATION, destNodeRef);
}
else if (this.action.equals(MoveActionExecuter.NAME))
{
NodeRef destNodeRef = (NodeRef)actionProps.get(MoveActionExecuter.PARAM_DESTINATION_FOLDER);
this.currentActionProperties.put(PROP_DESTINATION, destNodeRef);
}
else if (this.action.equals(SimpleWorkflowActionExecuter.NAME))
{
String approveStep = (String)actionProps.get(SimpleWorkflowActionExecuter.PARAM_APPROVE_STEP);
Boolean approveMove = (Boolean)actionProps.get(SimpleWorkflowActionExecuter.PARAM_APPROVE_MOVE);
NodeRef approveFolderNode = (NodeRef)actionProps.get(
SimpleWorkflowActionExecuter.PARAM_APPROVE_FOLDER);
String rejectStep = (String)actionProps.get(SimpleWorkflowActionExecuter.PARAM_REJECT_STEP);
Boolean rejectMove = (Boolean)actionProps.get(SimpleWorkflowActionExecuter.PARAM_REJECT_MOVE);
NodeRef rejectFolderNode = (NodeRef)actionProps.get(
SimpleWorkflowActionExecuter.PARAM_REJECT_FOLDER);
this.currentActionProperties.put(PROP_APPROVE_STEP_NAME, approveStep);
this.currentActionProperties.put(PROP_APPROVE_ACTION, approveMove ? "move" : "copy");
this.currentActionProperties.put(PROP_APPROVE_FOLDER, approveFolderNode);
if (rejectStep == null && rejectMove == null && rejectFolderNode == null)
{
this.currentActionProperties.put(PROP_REJECT_STEP_PRESENT, "no");
}
else
{
this.currentActionProperties.put(PROP_REJECT_STEP_PRESENT, "yes");
this.currentActionProperties.put(PROP_REJECT_STEP_NAME, rejectStep);
this.currentActionProperties.put(PROP_REJECT_ACTION, rejectMove ? "move" : "copy");
this.currentActionProperties.put(PROP_REJECT_FOLDER, rejectFolderNode);
}
}
else if (this.action.equals(LinkCategoryActionExecuter.NAME))
{
NodeRef catNodeRef = (NodeRef)actionProps.get(LinkCategoryActionExecuter.PARAM_CATEGORY_VALUE);
this.currentActionProperties.put(PROP_CATEGORY, catNodeRef);
}
else if (this.action.equals(CheckOutActionExecuter.NAME))
{
NodeRef destNodeRef = (NodeRef)actionProps.get(CheckOutActionExecuter.PARAM_DESTINATION_FOLDER);
this.currentActionProperties.put(PROP_DESTINATION, destNodeRef);
}
else if (this.action.equals(CheckInActionExecuter.NAME))
{
String checkDesc = (String)actionProps.get(CheckInActionExecuter.PARAM_DESCRIPTION);
this.currentActionProperties.put(PROP_CHECKIN_DESC, checkDesc);
Boolean minorChange = (Boolean)actionProps.get(CheckInActionExecuter.PARAM_MINOR_CHANGE);
this.currentActionProperties.put(PROP_CHECKIN_MINOR, minorChange);
}
else if (this.action.equals(TransformActionExecuter.NAME))
{
String transformer = (String)actionProps.get(TransformActionExecuter.PARAM_MIME_TYPE);
this.currentActionProperties.put(PROP_TRANSFORMER, transformer);
NodeRef destNodeRef = (NodeRef)actionProps.get(CopyActionExecuter.PARAM_DESTINATION_FOLDER);
this.currentActionProperties.put(PROP_DESTINATION, destNodeRef);
}
else if (this.action.equals(ImageTransformActionExecuter.NAME))
{
String transformer = (String)actionProps.get(TransformActionExecuter.PARAM_MIME_TYPE);
this.currentActionProperties.put(PROP_IMAGE_TRANSFORMER, transformer);
String options = (String)actionProps.get(ImageTransformActionExecuter.PARAM_CONVERT_COMMAND);
this.currentActionProperties.put(PROP_TRANSFORM_OPTIONS, options != null ? options : "");
NodeRef destNodeRef = (NodeRef)actionProps.get(CopyActionExecuter.PARAM_DESTINATION_FOLDER);
this.currentActionProperties.put(PROP_DESTINATION, destNodeRef);
}
else if (this.action.equals(MailActionExecuter.NAME))
{
String subject = (String)actionProps.get(MailActionExecuter.PARAM_SUBJECT);
this.currentActionProperties.put(PROP_SUBJECT, subject);
String message = (String)actionProps.get(MailActionExecuter.PARAM_TEXT);
this.currentActionProperties.put(PROP_MESSAGE, message);
String to = (String)actionProps.get(MailActionExecuter.PARAM_TO);
this.currentActionProperties.put(PROP_TO, to);
}
else if (this.action.equals(ImporterActionExecuter.NAME))
{
NodeRef destNodeRef = (NodeRef)actionProps.get(ImporterActionExecuter.PARAM_DESTINATION_FOLDER);
this.currentActionProperties.put(PROP_DESTINATION, destNodeRef);
}
else if (this.action.equals(SpecialiseTypeActionExecuter.NAME) == true)
{
QName specialiseType = (QName)actionProps.get(SpecialiseTypeActionExecuter.PARAM_TYPE_NAME);
this.currentActionProperties.put(PROP_OBJECT_TYPE, specialiseType.toString());
}
}
/**
* @return Returns the selected action
*/
public String getAction()
{
return this.action;
}
/**
* @param action Sets the selected action
*/
public void setAction(String action)
{
this.action = action;
}
/**
* Sets the action service
*
* @param actionRegistration the action service
*/
public void setActionService(ActionService actionService)
{
this.actionService = actionService;
}
/**
* Sets the dictionary service
*
* @param dictionaryService the dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* Sets the mimetype service
*
* @param mimetypeService The mimetype service
*/
public void setMimetypeService(MimetypeService mimetypeService)
{
this.mimetypeService = mimetypeService;
}
/**
* @return Returns the list of selectable actions
*/
public List<SelectItem> getActions()
{
if (this.actions == null)
{
List<ActionDefinition> ruleActions = this.actionService.getActionDefinitions();
this.actions = new ArrayList<SelectItem>();
for (ActionDefinition ruleActionDef : ruleActions)
{
this.actions.add(new SelectItem(ruleActionDef.getName(), ruleActionDef.getTitle()));
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.actions, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
return this.actions;
}
/**
* @return Returns a map of all the action descriptions
*/
public Map<String, String> getActionDescriptions()
{
if (this.actionDescriptions == null)
{
List<ActionDefinition> ruleActions = this.actionService.getActionDefinitions();
this.actionDescriptions = new HashMap<String, String>();
for (ActionDefinition ruleActionDef : ruleActions)
{
this.actionDescriptions.put(ruleActionDef.getName(), ruleActionDef.getDescription());
}
}
return this.actionDescriptions;
}
/**
* @return Gets the action settings
*/
public Map<String, Serializable> getActionProperties()
{
return this.currentActionProperties;
}
/**
* Returns a list of encodings the import and export actions can use
*
* @return List of SelectItem objects representing the available encodings
*/
public List<SelectItem> getEncodings()
{
if (this.encodings == null)
{
ConfigService svc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
Config wizardCfg = svc.getConfig("Action Wizards");
if (wizardCfg != null)
{
ConfigElement encodingsCfg = wizardCfg.getConfigElement("encodings");
if (encodingsCfg != null)
{
FacesContext context = FacesContext.getCurrentInstance();
this.encodings = new ArrayList<SelectItem>();
for (ConfigElement child : encodingsCfg.getChildren())
{
String id = child.getAttribute("name");
// look for a client localized string
String label = null;
String msgId = child.getAttribute("displayLabelId");
if (msgId != null)
{
label = Application.getMessage(context, msgId);
}
// if there wasn't an externalized string look for one in the config
if (label == null)
{
label = child.getAttribute("displayLabel");
}
this.encodings.add(new SelectItem(id, label));
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.encodings, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
else
{
logger.warn("Could not find encodings configuration element");
}
}
else
{
logger.warn("Could not find Action Wizards configuration section");
}
}
return this.encodings;
}
/**
* Returns the transformers that are available
*
* @return List of SelectItem objects representing the available transformers
*/
public List<SelectItem> getTransformers()
{
if (this.transformers == null)
{
ConfigService svc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
Config wizardCfg = svc.getConfig("Action Wizards");
if (wizardCfg != null)
{
ConfigElement transformersCfg = wizardCfg.getConfigElement("transformers");
if (transformersCfg != null)
{
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> mimeTypes = this.mimetypeService.getDisplaysByMimetype();
this.transformers = new ArrayList<SelectItem>();
for (ConfigElement child : transformersCfg.getChildren())
{
String id = child.getAttribute("name");
// look for a client localized string
String label = null;
String msgId = child.getAttribute("displayLabelId");
if (msgId != null)
{
label = Application.getMessage(context, msgId);
}
// if there wasn't an externalized string look for one in the config
if (label == null)
{
label = child.getAttribute("displayLabel");
}
// if there wasn't a client based label get it from the mime type service
if (label == null)
{
label = mimeTypes.get(id);
}
this.transformers.add(new SelectItem(id, label));
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.transformers, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
else
{
logger.warn("Could not find transformers configuration element");
}
}
else
{
logger.warn("Could not find Action Wizards configuration section");
}
}
return this.transformers;
}
/**
* Returns the image transformers that are available
*
* @return List of SelectItem objects representing the available image transformers
*/
public List<SelectItem> getImageTransformers()
{
if (this.imageTransformers == null)
{
ConfigService svc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
Config wizardCfg = svc.getConfig("Action Wizards");
if (wizardCfg != null)
{
ConfigElement transformersCfg = wizardCfg.getConfigElement("image-transformers");
if (transformersCfg != null)
{
FacesContext context = FacesContext.getCurrentInstance();
Map<String, String> mimeTypes = this.mimetypeService.getDisplaysByMimetype();
this.imageTransformers = new ArrayList<SelectItem>();
for (ConfigElement child : transformersCfg.getChildren())
{
String id = child.getAttribute("name");
// look for a client localized string
String label = null;
String msgId = child.getAttribute("displayLabelId");
if (msgId != null)
{
label = Application.getMessage(context, msgId);
}
// if there wasn't an externalized string look for one in the config
if (label == null)
{
label = child.getAttribute("displayLabel");
}
// if there wasn't a client based label get it from the mime type service
if (label == null)
{
label = mimeTypes.get(id);
}
this.imageTransformers.add(new SelectItem(id, label));
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.imageTransformers, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
else
{
logger.warn("Could not find image-transformers configuration element");
}
}
else
{
logger.warn("Could not find Action Wizards configuration section");
}
}
return this.imageTransformers;
}
/**
* Returns the aspects that are available
*
* @return List of SelectItem objects representing the available aspects
*/
public List<SelectItem> getAspects()
{
if (this.aspects == null)
{
ConfigService svc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
Config wizardCfg = svc.getConfig("Action Wizards");
if (wizardCfg != null)
{
ConfigElement aspectsCfg = wizardCfg.getConfigElement("aspects");
if (aspectsCfg != null)
{
FacesContext context = FacesContext.getCurrentInstance();
this.aspects = new ArrayList<SelectItem>();
for (ConfigElement child : aspectsCfg.getChildren())
{
QName idQName = Repository.resolveToQName(child.getAttribute("name"));
// look for a client localized string
String label = null;
String msgId = child.getAttribute("displayLabelId");
if (msgId != null)
{
label = Application.getMessage(context, msgId);
}
// if there wasn't an externalized string look for one in the config
if (label == null)
{
label = child.getAttribute("displayLabel");
}
// if there wasn't a client based label try and get it from the dictionary
if (label == null)
{
AspectDefinition aspectDef = this.dictionaryService.getAspect(idQName);
if (aspectDef != null)
{
label = aspectDef.getTitle();
}
else
{
label = idQName.getLocalName();
}
}
this.aspects.add(new SelectItem(idQName.toString(), label));
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.aspects, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
else
{
logger.warn("Could not find aspects configuration element");
}
}
else
{
logger.warn("Could not find Action Wizards configuration section");
}
}
return this.aspects;
}
/**
* @return Returns a list of object types to allow the user to select from
*/
public List<SelectItem> getObjectTypes()
{
if (this.objectTypes == null)
{
FacesContext context = FacesContext.getCurrentInstance();
// add the well known object type to start with
this.objectTypes = new ArrayList<SelectItem>(5);
this.objectTypes.add(new SelectItem(ContentModel.TYPE_CONTENT.toString(),
Application.getMessage(context, "content")));
// add any configured content sub-types to the list
ConfigService svc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
Config wizardCfg = svc.getConfig("Custom Content Types");
if (wizardCfg != null)
{
ConfigElement typesCfg = wizardCfg.getConfigElement("content-types");
if (typesCfg != null)
{
for (ConfigElement child : typesCfg.getChildren())
{
QName idQName = Repository.resolveToQName(child.getAttribute("name"));
TypeDefinition typeDef = this.dictionaryService.getType(idQName);
if (typeDef != null &&
this.dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_CONTENT))
{
// look for a client localized string
String label = null;
String msgId = child.getAttribute("displayLabelId");
if (msgId != null)
{
label = Application.getMessage(context, msgId);
}
// if there wasn't an externalized string look for one in the config
if (label == null)
{
label = child.getAttribute("displayLabel");
}
// if there wasn't a client based label try and get it from the dictionary
if (label == null)
{
label = typeDef.getTitle();
}
// finally, just use the localname
if (label == null)
{
label = idQName.getLocalName();
}
this.objectTypes.add(new SelectItem(idQName.toString(), label));
}
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.objectTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
else
{
logger.warn("Could not find 'content-types' configuration element");
}
}
else
{
logger.warn("Could not find 'Custom Content Types' configuration section");
}
}
return this.objectTypes;
}
/**
* @return the List of users in the system wrapped in SelectItem objects
*/
public List<SelectItem> getUsers()
{
if (this.users == null)
{
List<Node> userNodes = Repository.getUsers(
FacesContext.getCurrentInstance(),
this.nodeService,
this.searchService);
this.users = new ArrayList<SelectItem>();
for (Node user : userNodes)
{
String email = (String)user.getProperties().get("email");
if (email != null && email.length() > 0)
{
this.users.add(new SelectItem(email, (String)user.getProperties().get("fullName")));
}
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.users, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
return this.users;
}
}

View File

@@ -0,0 +1,611 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.io.File;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.transaction.UserTransaction;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.data.QuickSort;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Base Handler class used by the Content Wizards
*
* @author gavinc kevinr
*/
public abstract class BaseContentWizard extends AbstractWizardBean
{
private static Log logger = LogFactory.getLog(BaseContentWizard.class);
protected static final String FINISH_INSTRUCTION_ID = "content_finish_instruction";
// content wizard specific attributes
protected String fileName;
protected String author;
protected String title;
protected String description;
protected String contentType;
protected String objectType;
protected boolean inlineEdit;
protected List<SelectItem> contentTypes;
protected List<SelectItem> objectTypes;
protected ContentService contentService;
protected DictionaryService dictionaryService;
// the NodeRef of the node created during finish
protected NodeRef createdNode;
/**
* Save the specified content using the currently set wizard attributes
*
* @param fileContent File content to save
* @param strContent String content to save
*/
protected String saveContent(File fileContent, String strContent)
{
String outcome = FINISH_OUTCOME;
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
if (this.editMode)
{
// update the existing node in the repository
Node currentDocument = this.browseBean.getDocument();
NodeRef nodeRef = currentDocument.getNodeRef();
// move the file - location and name checks will be performed
this.fileFolderService.move(nodeRef, null, this.fileName);
// set up the content data
// update the modified timestamp and other content props
Map<QName, Serializable> contentProps = this.nodeService.getProperties(nodeRef);
contentProps.put(ContentModel.PROP_TITLE, this.title);
contentProps.put(ContentModel.PROP_DESCRIPTION, this.description);
contentProps.put(ContentModel.PROP_CREATOR, this.author);
// set up content properties - copy or create the compound property
ContentData contentData = (ContentData)contentProps.get(ContentModel.PROP_CONTENT);
if (contentData == null)
{
contentData = new ContentData(null, this.contentType, 0L, "UTF-8");
}
else
{
contentData = new ContentData(
contentData.getContentUrl(),
this.contentType,
contentData.getSize(),
contentData.getEncoding());
}
contentProps.put(ContentModel.PROP_CONTENT, contentData);
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_INLINEEDITABLE) == false)
{
Map<QName, Serializable> editProps = new HashMap<QName, Serializable>(1, 1.0f);
editProps.put(ContentModel.PROP_EDITINLINE, this.inlineEdit);
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_INLINEEDITABLE, editProps);
}
else
{
contentProps.put(ContentModel.PROP_EDITINLINE, this.inlineEdit);
}
this.nodeService.setProperties(nodeRef, contentProps);
}
else
{
// get the node ref of the node that will contain the content
NodeRef containerNodeRef;
String nodeId = getNavigator().getCurrentNodeId();
if (nodeId == null)
{
containerNodeRef = this.nodeService.getRootNode(Repository.getStoreRef());
}
else
{
containerNodeRef = new NodeRef(Repository.getStoreRef(), nodeId);
}
FileInfo fileInfo = fileFolderService.create(
containerNodeRef,
this.fileName,
Repository.resolveToQName(this.objectType));
NodeRef fileNodeRef = fileInfo.getNodeRef();
// set the author (if we have)
if (this.author != null && this.author.length() > 0)
{
this.nodeService.setProperty(fileNodeRef, ContentModel.PROP_CREATOR, this.author);
}
if (logger.isDebugEnabled())
logger.debug("Created file node for file: " + this.fileName);
// apply the titled aspect - title and description
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>(3, 1.0f);
titledProps.put(ContentModel.PROP_TITLE, this.title);
titledProps.put(ContentModel.PROP_DESCRIPTION, this.description);
this.nodeService.addAspect(fileNodeRef, ContentModel.ASPECT_TITLED, titledProps);
if (logger.isDebugEnabled())
logger.debug("Added titled aspect with properties: " + titledProps);
// apply the inlineeditable aspect
if (this.inlineEdit == true)
{
Map<QName, Serializable> editProps = new HashMap<QName, Serializable>(1, 1.0f);
editProps.put(ContentModel.PROP_EDITINLINE, this.inlineEdit);
this.nodeService.addAspect(fileNodeRef, ContentModel.ASPECT_INLINEEDITABLE, editProps);
if (logger.isDebugEnabled())
logger.debug("Added inlineeditable aspect with properties: " + editProps);
}
// get a writer for the content and put the file
ContentWriter writer = contentService.getWriter(fileNodeRef, ContentModel.PROP_CONTENT, true);
// set the mimetype and encoding
writer.setMimetype(this.contentType);
writer.setEncoding("UTF-8");
if (fileContent != null)
{
writer.putContent(fileContent);
}
else if (strContent != null)
{
writer.putContent(strContent);
}
// remember the created node now
this.createdNode = fileNodeRef;
}
// give subclasses a chance to perform custom processing before committing
performCustomProcessing();
// commit the transaction
tx.commit();
}
catch (FileExistsException e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
// print status message
String statusMsg = MessageFormat.format(
Application.getMessage(
FacesContext.getCurrentInstance(), "error_exists"),
e.getExisting().getName());
Utils.addErrorMessage(statusMsg);
// no outcome
outcome = null;
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
outcome = null;
}
return outcome;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepInstructions()
*/
public String getStepInstructions()
{
String stepInstruction = null;
switch (this.currentStep)
{
case 3:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), FINISH_INSTRUCTION_ID);
break;
}
default:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), DEFAULT_INSTRUCTION_ID);
}
}
return stepInstruction;
}
/**
* Initialises the wizard
*/
public void init()
{
super.init();
this.fileName = null;
this.author = null;
this.title = null;
this.description = null;
this.contentType = null;
this.inlineEdit = false;
this.contentTypes = null;
this.objectTypes = null;
this.objectType = ContentModel.TYPE_CONTENT.toString();
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#populate()
*/
public void populate()
{
// get hold of the current document and populate the appropriate values
Node currentDocument = this.browseBean.getDocument();
Map<String, Object> props = currentDocument.getProperties();
Boolean inline = (Boolean)props.get("editInline");
this.inlineEdit = inline != null ? inline.booleanValue() : false;
this.author = (String)props.get("creator");
this.contentType = null;
ContentData contentData = (ContentData)props.get(ContentModel.PROP_CONTENT);
if (contentData != null)
{
this.contentType = contentData.getMimetype();
}
this.description = (String)props.get("description");
this.fileName = currentDocument.getName();
this.title = (String)props.get("title");
}
/**
* @return Returns the contentService.
*/
public ContentService getContentService()
{
return contentService;
}
/**
* @param contentService The contentService to set.
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* Sets the dictionary service
*
* @param dictionaryService the dictionary service
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* @return Returns the name of the file
*/
public String getFileName()
{
return this.fileName;
}
/**
* @param fileName The name of the file
*/
public void setFileName(String fileName)
{
this.fileName = fileName;
}
/**
* @return Returns the author
*/
public String getAuthor()
{
return this.author;
}
/**
* @param author Sets the author
*/
public void setAuthor(String author)
{
this.author = author;
}
/**
* @return Returns the content type currenty selected
*/
public String getContentType()
{
return this.contentType;
}
/**
* @param contentType Sets the currently selected content type
*/
public void setContentType(String contentType)
{
this.contentType = contentType;
}
/**
* @return Returns the object type currenty selected
*/
public String getObjectType()
{
return this.objectType;
}
/**
* @param objectType Sets the currently selected object type
*/
public void setObjectType(String objectType)
{
this.objectType = objectType;
}
/**
* @return Returns the description
*/
public String getDescription()
{
return this.description;
}
/**
* @param description Sets the description
*/
public void setDescription(String description)
{
this.description = description;
}
/**
* @return Returns the title
*/
public String getTitle()
{
return this.title;
}
/**
* @param title Sets the title
*/
public void setTitle(String title)
{
this.title = title;
}
/**
* @return Returns the inline edit flag.
*/
public boolean isInlineEdit()
{
return this.inlineEdit;
}
/**
* @param inlineEdit The inline edit flag to set.
*/
public void setInlineEdit(boolean inlineEdit)
{
this.inlineEdit = inlineEdit;
}
/**
* @return Returns a list of content types to allow the user to select from
*/
public List<SelectItem> getContentTypes()
{
if (this.contentTypes == null)
{
this.contentTypes = new ArrayList<SelectItem>(80);
ServiceRegistry registry = Repository.getServiceRegistry(FacesContext.getCurrentInstance());
MimetypeService mimetypeService = registry.getMimetypeService();
// get the mime type display names
Map<String, String> mimeTypes = mimetypeService.getDisplaysByMimetype();
for (String mimeType : mimeTypes.keySet())
{
this.contentTypes.add(new SelectItem(mimeType, mimeTypes.get(mimeType)));
}
// make sure the list is sorted by the values
QuickSort sorter = new QuickSort(this.contentTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
return this.contentTypes;
}
/**
* @return Returns a list of object types to allow the user to select from
*/
public List<SelectItem> getObjectTypes()
{
if (this.objectTypes == null)
{
FacesContext context = FacesContext.getCurrentInstance();
// add the well known object type to start with
this.objectTypes = new ArrayList<SelectItem>(5);
this.objectTypes.add(new SelectItem(ContentModel.TYPE_CONTENT.toString(),
Application.getMessage(context, "content")));
// add any configured content sub-types to the list
ConfigService svc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
FacesContext.getCurrentInstance()).getBean(Application.BEAN_CONFIG_SERVICE);
Config wizardCfg = svc.getConfig("Custom Content Types");
if (wizardCfg != null)
{
ConfigElement typesCfg = wizardCfg.getConfigElement("content-types");
if (typesCfg != null)
{
for (ConfigElement child : typesCfg.getChildren())
{
QName idQName = Repository.resolveToQName(child.getAttribute("name"));
TypeDefinition typeDef = this.dictionaryService.getType(idQName);
if (typeDef != null &&
this.dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_CONTENT))
{
// look for a client localized string
String label = null;
String msgId = child.getAttribute("displayLabelId");
if (msgId != null)
{
label = Application.getMessage(context, msgId);
}
// if there wasn't an externalized string look for one in the config
if (label == null)
{
label = child.getAttribute("displayLabel");
}
// if there wasn't a client based label try and get it from the dictionary
if (label == null)
{
label = typeDef.getTitle();
}
// finally, just use the localname
if (label == null)
{
label = idQName.getLocalName();
}
this.objectTypes.add(new SelectItem(idQName.toString(), label));
}
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(this.objectTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
else
{
logger.warn("Could not find 'content-types' configuration element");
}
}
else
{
logger.warn("Could not find 'Custom Content Types' configuration section");
}
}
return this.objectTypes;
}
/**
* @return Determines whether the next and finish button should be enabled
*/
public boolean getNextFinishDisabled()
{
boolean disabled = false;
if (this.fileName == null || this.fileName.length() == 0 ||
this.title == null || this.title.length() == 0 ||
this.contentType == null)
{
disabled = true;
}
return disabled;
}
/**
* Returns the display label for the content type currently chosen
*
* @return The human readable version of the content type
*/
protected String getSummaryContentType()
{
ServiceRegistry registry = Repository.getServiceRegistry(FacesContext.getCurrentInstance());
MimetypeService mimetypeService = registry.getMimetypeService();
// get the mime type display name
Map<String, String> mimeTypes = mimetypeService.getDisplaysByMimetype();
return mimeTypes.get(this.contentType);
}
/**
* Returns the display label for the currently selected object type
*
* @return The objevt type label
*/
protected String getSummaryObjectType()
{
String objType = null;
for (SelectItem item : this.getObjectTypes())
{
if (item.getValue().equals(this.objectType))
{
objType = item.getLabel();
break;
}
}
return objType;
}
/**
* Performs any processing sub classes may wish to do before commit is called
*/
protected void performCustomProcessing()
{
// used by subclasses if necessary
}
}

View File

@@ -0,0 +1,300 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.util.ResourceBundle;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Handler class used by the Create In-line Content Wizard
*
* @author Kevin Roast
*/
public class CreateContentWizard extends BaseContentWizard
{
protected static final String CONTENT_TEXT = "txt";
protected static final String CONTENT_HTML = "html";
private static Log logger = LogFactory.getLog(CreateContentWizard.class);
// TODO: retrieve these from the config service
private static final String WIZARD_TITLE_ID = "create_content_title";
private static final String WIZARD_DESC_ID = "create_content_desc";
private static final String STEP1_TITLE_ID = "create_content_step1_title";
private static final String STEP1_DESCRIPTION_ID = "create_content_step1_desc";
private static final String STEP2_TITLE_ID = "create_content_step2_title";
private static final String STEP2_DESCRIPTION_ID = "create_content_step2_desc";
private static final String STEP3_TITLE_ID = "create_content_step3_title";
private static final String STEP3_DESCRIPTION_ID = "create_content_step3_desc";
// create content wizard specific properties
protected String content;
protected String createType = CONTENT_HTML;
/**
* Deals with the finish button being pressed
*
* @return outcome
*/
public String finish()
{
return saveContent(null, this.content);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardDescription()
*/
public String getWizardDescription()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_DESC_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardTitle()
*/
public String getWizardTitle()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_TITLE_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription()
*/
public String getStepDescription()
{
String stepDesc = null;
switch (this.currentStep)
{
case 1:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_DESCRIPTION_ID);
break;
}
case 2:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_DESCRIPTION_ID);
break;
}
case 3:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP3_DESCRIPTION_ID);
break;
}
case 4:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), SUMMARY_DESCRIPTION_ID);
break;
}
default:
{
stepDesc = "";
}
}
return stepDesc;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepInstructions()
*/
public String getStepInstructions()
{
String stepInstruction = null;
switch (this.currentStep)
{
case 4:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), FINISH_INSTRUCTION_ID);
break;
}
default:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), DEFAULT_INSTRUCTION_ID);
}
}
return stepInstruction;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepTitle()
*/
public String getStepTitle()
{
String stepTitle = null;
switch (this.currentStep)
{
case 1:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_TITLE_ID);
break;
}
case 2:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_TITLE_ID);
break;
}
case 3:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP3_TITLE_ID);
break;
}
case 4:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), SUMMARY_TITLE_ID);
break;
}
default:
{
stepTitle = "";
}
}
return stepTitle;
}
/**
* @return Returns the content from the edited form.
*/
public String getContent()
{
return this.content;
}
/**
* @param content The content to edit (should be clear initially)
*/
public void setContent(String content)
{
this.content = content;
}
/**
* Initialises the wizard
*/
public void init()
{
super.init();
this.content = null;
// created content is inline editable by default
this.inlineEdit = true;
}
/**
* @return Returns the summary data for the wizard.
*/
public String getSummary()
{
ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
// TODO: show first few lines of content here?
return buildSummary(
new String[] {bundle.getString("file_name"), bundle.getString("type"),
bundle.getString("content_type"), bundle.getString("title"),
bundle.getString("description"), bundle.getString("author")},
new String[] {this.fileName, getSummaryObjectType(), getSummaryContentType(),
this.title, this.description, this.author});
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#determineOutcomeForStep(int)
*/
protected String determineOutcomeForStep(int step)
{
String outcome = null;
switch(step)
{
case 1:
{
outcome = "select";
break;
}
case 2:
{
if (getCreateType().equals(CONTENT_HTML))
{
outcome = "create-html";
}
else if (getCreateType().equals(CONTENT_TEXT))
{
outcome = "create-text";
}
break;
}
case 3:
{
this.fileName = "newfile." + getCreateType();
this.contentType = Repository.getMimeTypeForFileName(
FacesContext.getCurrentInstance(), this.fileName);
this.title = this.fileName;
outcome = "properties";
break;
}
case 4:
{
outcome = "summary";
break;
}
default:
{
outcome = CANCEL_OUTCOME;
}
}
return outcome;
}
/**
* Create content type value changed by the user
*/
public void createContentChanged(ValueChangeEvent event)
{
// clear the content as HTML is not compatible with the plain text box etc.
this.content = null;
}
/**
* @return Returns the createType.
*/
public String getCreateType()
{
return this.createType;
}
/**
* @param createType The createType to set.
*/
public void setCreateType(String createType)
{
this.createType = createType;
}
}

View File

@@ -0,0 +1,806 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import javax.faces.component.UISelectOne;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.ui.common.SortableSelectItem;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIGenericPicker;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
/**
* @author Kevin Roast
*/
public class InviteUsersWizard extends AbstractWizardBean
{
private static Log logger = LogFactory.getLog(InviteUsersWizard.class);
/** I18N message strings */
private static final String MSG_USERS = "users";
private static final String MSG_GROUPS = "groups";
private static final String MSG_INVITED_SPACE = "invite_space";
private static final String MSG_INVITED_ROLE = "invite_role";
private static final String WIZARD_TITLE_ID = "invite_title";
private static final String WIZARD_DESC_ID = "invite_desc";
private static final String STEP1_TITLE_ID = "invite_step1_title";
private static final String STEP1_DESCRIPTION_ID = "invite_step1_desc";
private static final String STEP2_TITLE_ID = "invite_step2_title";
private static final String STEP2_DESCRIPTION_ID = "invite_step2_desc";
private static final String FINISH_INSTRUCTION_ID = "invite_finish_instruction";
private static final String NOTIFY_YES = "yes";
/** NamespaceService bean reference */
private NamespaceService namespaceService;
/** JavaMailSender bean reference */
private JavaMailSender mailSender;
/** AuthorityService bean reference */
private AuthorityService authorityService;
/** PermissionService bean reference */
private PermissionService permissionService;
/** personService bean reference */
private PersonService personService;
/** datamodel for table of roles for users */
private DataModel userRolesDataModel = null;
/** list of user/group role wrapper objects */
private List<UserGroupRole> userGroupRoles = null;
/** Cache of available folder permissions */
Set<String> folderPermissions = null;
/** dialog state */
private String notify = NOTIFY_YES;
private String subject = null;
private String body = null;
private String internalSubject = null;
private String automaticText = null;
/**
* @param namespaceService The NamespaceService to set.
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param mailSender The JavaMailSender to set.
*/
public void setMailSender(JavaMailSender mailSender)
{
this.mailSender = mailSender;
}
/**
* @param permissionService The PermissionService to set.
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* @param permissionService The PermissionService to set.
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* @param authorityService The authorityService to set.
*/
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
/**
* Initialises the wizard
*/
public void init()
{
super.init();
notify = NOTIFY_YES;
userGroupRoles = new ArrayList<UserGroupRole>(8);
subject = "";
body = "";
automaticText = "";
internalSubject = null;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#finish()
*/
public String finish()
{
String outcome = FINISH_OUTCOME;
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
String subject = this.subject;
if (subject == null || subject.length() == 0)
{
subject = this.internalSubject;
}
User user = Application.getCurrentUser(context);
String from = (String)this.nodeService.getProperty(user.getPerson(), ContentModel.PROP_EMAIL);
if (from == null || from.length() == 0)
{
// TODO: get this from spring config?
from = "alfresco@alfresco.org";
}
// get the Space to apply changes too
NodeRef folderNodeRef = this.browseBean.getActionSpace().getNodeRef();
// set permissions for each user and send them a mail
for (int i=0; i<this.userGroupRoles.size(); i++)
{
UserGroupRole userGroupRole = this.userGroupRoles.get(i);
String authority = userGroupRole.getAuthority();
// find the selected permission ref from it's name and apply for the specified user
Set<String> perms = getFolderPermissions();
for (String permission : perms)
{
if (userGroupRole.getRole().equals(permission))
{
this.permissionService.setPermission(
folderNodeRef,
authority,
permission,
true);
break;
}
}
// Create the mail message for sending to each User
if (NOTIFY_YES.equals(this.notify))
{
// if User, email then, else if Group get all members and email them
AuthorityType authType = AuthorityType.getAuthorityType(authority);
if (authType.equals(AuthorityType.USER))
{
if (this.personService.personExists(authority) == true)
{
notifyUser(this.personService.getPerson(authority), folderNodeRef, from, userGroupRole.getRole());
}
}
else if (authType.equals(AuthorityType.GROUP))
{
// else notify all members of the group
Set<String> users = this.authorityService.getContainedAuthorities(AuthorityType.USER, authority, false);
for (String userAuth : users)
{
if (this.personService.personExists(userAuth) == true)
{
notifyUser(this.personService.getPerson(userAuth), folderNodeRef, from, userGroupRole.getRole());
}
}
}
}
}
// commit the transaction
tx.commit();
UIContextService.getInstance(context).notifyBeans();
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), e.getMessage()), e);
outcome = null;
}
return outcome;
}
/**
* Send an email notification to the specified User authority
*
* @param person Person node representing the user
* @param folder Folder node they are invited too
* @param from From text message
* @param roleText The role display label for the user invite notification
*/
private void notifyUser(NodeRef person, NodeRef folder, String from, String roleText)
{
String to = (String)this.nodeService.getProperty(person, ContentModel.PROP_EMAIL);
if (to != null && to.length() != 0)
{
String msgRole = Application.getMessage(FacesContext.getCurrentInstance(), MSG_INVITED_ROLE);
String roleMessage = MessageFormat.format(msgRole, new Object[] {roleText});
// TODO: include External Authentication link to the invited space
//String args = folder.getStoreRef().getProtocol() + '/' +
// folder.getStoreRef().getIdentifier() + '/' +
// folder.getId();
//String url = ExternalAccessServlet.generateExternalURL(LoginBean.OUTCOME_SPACEDETAILS, args);
String body = this.internalSubject + "\r\n\r\n" + roleMessage + "\r\n\r\n";// + url + "\r\n\r\n";
if (this.body != null && this.body.length() != 0)
{
body += this.body;
}
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setTo(to);
simpleMailMessage.setSubject(subject);
simpleMailMessage.setText(body);
simpleMailMessage.setFrom(from);
if (logger.isDebugEnabled())
logger.debug("Sending notification email to: " + to + "\n...with subject:\n" + subject + "\n...with body:\n" + body);
try
{
// Send the message
this.mailSender.send(simpleMailMessage);
}
catch (Throwable e)
{
// don't stop the action but let admins know email is not getting sent
logger.error("Failed to send email to " + to, e);
}
}
}
/**
* Returns the properties for current user-roles JSF DataModel
*
* @return JSF DataModel representing the current user-roles
*/
public DataModel getUserRolesDataModel()
{
if (this.userRolesDataModel == null)
{
this.userRolesDataModel = new ListDataModel();
}
this.userRolesDataModel.setWrappedData(this.userGroupRoles);
return this.userRolesDataModel;
}
/**
* Query callback method executed by the Generic Picker component.
* This method is part of the contract to the Generic Picker, it is up to the backing bean
* to execute whatever query is appropriate and return the results.
*
* @param filterIndex Index of the filter drop-down selection
* @param contains Text from the contains textbox
*
* @return An array of SelectItem objects containing the results to display in the picker.
*/
public SelectItem[] pickerCallback(int filterIndex, String contains)
{
FacesContext context = FacesContext.getCurrentInstance();
SelectItem[] items;
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(context, true);
tx.begin();
if (filterIndex == 0)
{
// build xpath to match available User/Person objects
NodeRef peopleRef = personService.getPeopleContainer();
// NOTE: see SearcherComponentTest
String xpath = "*[like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "firstName, '%" + contains + "%', false)" +
" or " + "like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "lastName, '%" + contains + "%', false)]";
List<NodeRef> nodes = searchService.selectNodes(
peopleRef,
xpath,
null,
this.namespaceService,
false);
items = new SelectItem[nodes.size()];
for (int index=0; index<nodes.size(); index++)
{
NodeRef personRef = nodes.get(index);
String firstName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME);
String lastName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME);
String username = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_USERNAME);
SelectItem item = new SortableSelectItem(username, firstName + " " + lastName, lastName);
items[index] = item;
}
}
else
{
// groups - simple text based match on name
Set<String> groups = authorityService.getAllAuthorities(AuthorityType.GROUP);
groups.addAll(authorityService.getAllAuthorities(AuthorityType.EVERYONE));
List<SelectItem> results = new ArrayList<SelectItem>(groups.size());
String containsLower = contains.toLowerCase();
int offset = PermissionService.GROUP_PREFIX.length();
for (String group : groups)
{
if (group.toLowerCase().indexOf(containsLower) != -1)
{
results.add(new SortableSelectItem(group, group.substring(offset), group));
}
}
items = new SelectItem[results.size()];
results.toArray(items);
}
Arrays.sort(items);
// commit the transaction
tx.commit();
}
catch (Exception err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err );
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
items = new SelectItem[0];
}
return items;
}
/**
* Action handler called when the Add button is pressed to process the current selection
*/
public void addSelection(ActionEvent event)
{
UIGenericPicker picker = (UIGenericPicker)event.getComponent().findComponent("picker");
UISelectOne rolePicker = (UISelectOne)event.getComponent().findComponent("roles");
String[] results = picker.getSelectedResults();
if (results != null)
{
String role = (String)rolePicker.getValue();
if (role != null)
{
for (int i=0; i<results.length; i++)
{
String authority = results[i];
// only add if authority not already present in the list with same role
boolean foundExisting = false;
for (int n=0; n<this.userGroupRoles.size(); n++)
{
UserGroupRole wrapper = this.userGroupRoles.get(n);
if (authority.equals(wrapper.getAuthority()) &&
role.equals(wrapper.getRole()))
{
foundExisting = true;
break;
}
}
if (foundExisting == false)
{
StringBuilder label = new StringBuilder(64);
// build a display label showing the user and their role for the space
AuthorityType authType = AuthorityType.getAuthorityType(authority);
if (authType.equals(AuthorityType.USER))
{
if (this.personService.personExists(authority) == true)
{
// found a User authority
NodeRef ref = this.personService.getPerson(authority);
String firstName = (String)this.nodeService.getProperty(ref, ContentModel.PROP_FIRSTNAME);
String lastName = (String)this.nodeService.getProperty(ref, ContentModel.PROP_LASTNAME);
label.append(firstName)
.append(" ")
.append(lastName)
.append(" (")
.append(Application.getMessage(FacesContext.getCurrentInstance(), role))
.append(")");
}
}
else
{
// found a group authority
label.append(authority.substring(PermissionService.GROUP_PREFIX.length()));
}
this.userGroupRoles.add(new UserGroupRole(authority, role, label.toString()));
}
}
}
}
}
/**
* Action handler called when the Remove button is pressed to remove a user+role
*/
public void removeSelection(ActionEvent event)
{
UserGroupRole wrapper = (UserGroupRole)this.userRolesDataModel.getRowData();
if (wrapper != null)
{
this.userGroupRoles.remove(wrapper);
}
}
/**
* Property accessed by the Generic Picker component.
*
* @return the array of filter options to show in the users/groups picker
*/
public SelectItem[] getFilters()
{
ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
return new SelectItem[] {
new SelectItem("0", bundle.getString(MSG_USERS)),
new SelectItem("1", bundle.getString(MSG_GROUPS)) };
}
/**
* @return The list of available roles for the users/groups
*/
public SelectItem[] getRoles()
{
ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
// get available roles (grouped permissions) from the permission service
Set<String> perms = getFolderPermissions();
SelectItem[] roles = new SelectItem[perms.size()];
int index = 0;
for (String permission : perms)
{
String displayLabel = bundle.getString(permission);
roles[index++] = new SelectItem(permission, displayLabel);
}
return roles;
}
/**
* @return Returns the notify listbox selection.
*/
public String getNotify()
{
return this.notify;
}
/**
* @param notify The notify listbox selection to set.
*/
public void setNotify(String notify)
{
this.notify = notify;
}
/**
* @return Returns the automaticText.
*/
public String getAutomaticText()
{
return this.automaticText;
}
/**
* @param automaticText The automaticText to set.
*/
public void setAutomaticText(String automaticText)
{
this.automaticText = automaticText;
}
/**
* @return Returns the email body text.
*/
public String getBody()
{
return this.body;
}
/**
* @param body The email body text to set.
*/
public void setBody(String body)
{
this.body = body;
}
/**
* @return Returns the email subject text.
*/
public String getSubject()
{
return this.subject;
}
/**
* @param subject The email subject text to set.
*/
public void setSubject(String subject)
{
this.subject = subject;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardDescription()
*/
public String getWizardDescription()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_DESC_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardTitle()
*/
public String getWizardTitle()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_TITLE_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription()
*/
public String getStepDescription()
{
String stepDesc = null;
switch (this.currentStep)
{
case 1:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_DESCRIPTION_ID);
break;
}
case 2:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_DESCRIPTION_ID);
break;
}
default:
{
stepDesc = "";
}
}
return stepDesc;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepTitle()
*/
public String getStepTitle()
{
String stepTitle = null;
switch (this.currentStep)
{
case 1:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_TITLE_ID);
break;
}
case 2:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_TITLE_ID);
break;
}
default:
{
stepTitle = "";
}
}
return stepTitle;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepInstructions()
*/
public String getStepInstructions()
{
String stepInstruction = null;
switch (this.currentStep)
{
case 2:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), FINISH_INSTRUCTION_ID);
break;
}
default:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), DEFAULT_INSTRUCTION_ID);
}
}
return stepInstruction;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#next()
*/
public String next()
{
String outcome = super.next();
if (outcome.equals("notify"))
{
FacesContext context = FacesContext.getCurrentInstance();
// prepare automatic text for email and screen
StringBuilder buf = new StringBuilder(256);
String personName = Application.getCurrentUser(context).getFullName(getNodeService());
String msgInvite = Application.getMessage(context, MSG_INVITED_SPACE);
Node node = this.browseBean.getActionSpace();
String path = this.nodeService.getPath(node.getNodeRef()).toDisplayPath(this.nodeService);
buf.append(MessageFormat.format(msgInvite, new Object[] {
path + '/' + node.getName(),
personName}) );
this.internalSubject = buf.toString();
buf.append("<br>");
String msgRole = Application.getMessage(context, MSG_INVITED_ROLE);
String roleText;
if (this.userGroupRoles.size() != 0)
{
String roleMsg = Application.getMessage(context, userGroupRoles.get(0).getRole());
roleText = MessageFormat.format(msgRole, roleMsg);
}
else
{
roleText = MessageFormat.format(msgRole, "[role]");
}
buf.append(roleText);
this.automaticText = buf.toString();
}
return outcome;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#determineOutcomeForStep(int)
*/
protected String determineOutcomeForStep(int step)
{
String outcome = null;
switch(step)
{
case 1:
{
outcome = "invite";
break;
}
case 2:
{
outcome = "notify";
break;
}
default:
{
outcome = CANCEL_OUTCOME;
}
}
return outcome;
}
/**
* @return a cached list of available folder permissions
*/
private Set<String> getFolderPermissions()
{
if (this.folderPermissions == null)
{
this.folderPermissions = this.permissionService.getSettablePermissions(ContentModel.TYPE_FOLDER);
}
return this.folderPermissions;
}
/**
* Simple wrapper class to represent a user/group and a role combination
*/
public static class UserGroupRole
{
public UserGroupRole(String authority, String role, String label)
{
this.authority = authority;
this.role = role;
this.label = label;
}
public String getAuthority()
{
return this.authority;
}
public String getRole()
{
return this.role;
}
public String getLabel()
{
return this.label;
}
private String authority;
private String role;
private String label;
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.transaction.UserTransaction;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Handler class used by the New Action Wizard
*
* @author Kevin Roast
*/
public class NewActionWizard extends BaseActionWizard
{
private static Log logger = LogFactory.getLog(NewActionWizard.class);
private static final String ERROR = "error_action";
// TODO: retrieve these from the config service
private static final String WIZARD_TITLE_ID = "create_action_title";
private static final String WIZARD_DESC_ID = "create_action_desc";
private static final String STEP1_TITLE_ID = "create_action_step1_title";
private static final String STEP2_TITLE_ID = "create_action_step2_title";
private static final String FINISH_INSTRUCTION_ID = "create_action_finish_instruction";
/**
* Deals with the finish button being pressed
*
* @return outcome
*/
public String finish()
{
String outcome = FINISH_OUTCOME;
UserTransaction tx = null;
try
{
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance());
tx.begin();
// build the action params map based on the selected action instance
Map<String, Serializable> actionParams = buildActionParams();
// build the action to execute
Action action = this.actionService.createAction(getAction());
action.setParameterValues(actionParams);
// execute the action on the current document node
this.actionService.executeAction(action, this.browseBean.getDocument().getNodeRef());
if (logger.isDebugEnabled())
{
logger.debug("Executed action '" + this.action +
"' with action params of " +
this.currentActionProperties);
}
// reset the current document properties/aspects in case we have changed them
// during the execution of the custom action
this.browseBean.getDocument().reset();
// commit the transaction
tx.commit();
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), ERROR), e.getMessage()), e);
outcome = null;
}
return outcome;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardDescription()
*/
public String getWizardDescription()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_DESC_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardTitle()
*/
public String getWizardTitle()
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_TITLE_ID);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription()
*/
public String getStepDescription()
{
return "";
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepTitle()
*/
public String getStepTitle()
{
String stepTitle = null;
switch (this.currentStep)
{
case 1:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_TITLE_ID);
break;
}
case 2:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_TITLE_ID);
break;
}
case 3:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), SUMMARY_TITLE_ID);
break;
}
default:
{
stepTitle = "";
}
}
return stepTitle;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepInstructions()
*/
public String getStepInstructions()
{
String stepInstruction = null;
switch (this.currentStep)
{
case 3:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), FINISH_INSTRUCTION_ID);
break;
}
default:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), DEFAULT_INSTRUCTION_ID);
}
}
return stepInstruction;
}
/**
* Initialises the wizard
*/
public void init()
{
super.init();
}
/**
* @return Returns the summary data for the wizard.
*/
public String getSummary()
{
String summaryAction = this.actionService.getActionDefinition(
this.action).getTitle();
return buildSummary(
new String[] {"Action"},
new String[] {summaryAction});
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#determineOutcomeForStep(int)
*/
protected String determineOutcomeForStep(int step)
{
String outcome = null;
switch(step)
{
case 1:
{
outcome = "action";
break;
}
case 2:
{
outcome = this.action;
break;
}
case 3:
{
outcome = "summary";
break;
}
default:
{
outcome = CANCEL_OUTCOME;
}
}
return outcome;
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.util.ArrayList;
import java.util.List;
import javax.faces.context.FacesContext;
import org.alfresco.model.ForumModel;
import org.alfresco.web.ui.common.component.UIListItem;
/**
* Wizard bean used for creating and editing forum spaces
*
* @author gavinc
*/
public class NewForumWizard extends NewSpaceWizard
{
public static final String FORUM_ICON_DEFAULT = "forum_large";
protected String forumStatus;
protected List<UIListItem> forumIcons;
/**
* Returns the status of the forum
*
* @return The status of the forum
*/
public String getForumStatus()
{
return this.forumStatus;
}
/**
* Sets the status of the forum
*
* @param forumStatus The status
*/
public void setForumStatus(String forumStatus)
{
this.forumStatus = forumStatus;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#init()
*/
public void init()
{
super.init();
this.spaceType = ForumModel.TYPE_FORUM.toString();
this.icon = FORUM_ICON_DEFAULT;
this.forumStatus = "0";
}
/**
* Returns a list of icons to allow the user to select from.
*
* @return A list of icons
*/
@SuppressWarnings("unchecked")
public List<UIListItem> getIcons()
{
// return the various forum icons
if (this.forumIcons == null)
{
this.forumIcons = new ArrayList<UIListItem>(1);
UIListItem item = new UIListItem();
item.setValue(FORUM_ICON_DEFAULT);
item.getAttributes().put("image", "/images/icons/forum_large.gif");
this.forumIcons.add(item);
}
return this.forumIcons;
}
/**
* @see org.alfresco.web.bean.wizard.NewSpaceWizard#performCustomProcessing(javax.faces.context.FacesContext)
*/
@Override
protected void performCustomProcessing(FacesContext context)
{
// add or update the ForumModel.PROP_STATUS property depending on the editMode
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.model.ForumModel;
import org.alfresco.web.ui.common.component.UIListItem;
/**
* Wizard bean used for creating and editing forums spaces
*
* @author gavinc
*/
public class NewForumsWizard extends NewSpaceWizard
{
public static final String FORUMS_ICON_DEFAULT = "forums_large";
protected List<UIListItem> forumsIcons;
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#init()
*/
public void init()
{
super.init();
this.spaceType = ForumModel.TYPE_FORUMS.toString();
this.icon = FORUMS_ICON_DEFAULT;
}
/**
* Returns a list of icons to allow the user to select from.
*
* @return A list of icons
*/
@SuppressWarnings("unchecked")
public List<UIListItem> getIcons()
{
// return the various forums icons
if (this.forumsIcons == null)
{
this.forumsIcons = new ArrayList<UIListItem>(1);
UIListItem item = new UIListItem();
item.setValue(FORUMS_ICON_DEFAULT);
item.getAttributes().put("image", "/images/icons/forums_large.gif");
this.forumsIcons.add(item);
}
return this.forumsIcons;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import javax.faces.context.FacesContext;
import org.alfresco.model.ForumModel;
import org.alfresco.util.GUID;
import org.alfresco.web.bean.repository.Repository;
/**
* Backing bean for posting forum articles.
*
* @author gavinc
*/
public class NewPostWizard extends CreateContentWizard
{
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#init()
*/
@Override
public void init()
{
super.init();
// set up for creating a post instead of HTML
this.createType = CONTENT_TEXT;
this.objectType = ForumModel.TYPE_POST.toString();
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#finish()
*/
@Override
public String finish()
{
// create appropriate values for filename, title and content type
this.fileName = GUID.generate() + ".txt";
this.contentType = Repository.getMimeTypeForFileName(
FacesContext.getCurrentInstance(), this.fileName);
this.title = this.fileName;
return super.finish();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import javax.faces.event.ActionEvent;
import org.alfresco.model.ContentModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Backing bean for posting replies to forum articles.
*
* @author gavinc
*/
public class NewReplyWizard extends NewPostWizard
{
private static Log logger = LogFactory.getLog(NewReplyWizard.class);
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#startWizard(javax.faces.event.ActionEvent)
*/
@Override
public void startWizard(ActionEvent event)
{
super.startWizard(event);
// also setup the content in the browse bean
this.browseBean.setupContentAction(event);
}
/**
* @see org.alfresco.web.bean.wizard.BaseContentWizard#performCustomProcessing()
*/
@Override
protected void performCustomProcessing()
{
if (this.editMode == false)
{
// setup the referencing aspect with the references association
// between the new post and the one being replied to
this.nodeService.addAspect(this.createdNode, ContentModel.ASPECT_REFERENCING, null);
this.nodeService.createAssociation(this.createdNode, this.browseBean.getDocument().getNodeRef(),
ContentModel.ASSOC_REFERENCES);
if (logger.isDebugEnabled())
{
logger.debug("created new node: " + this.createdNode);
logger.debug("existing node: " + this.browseBean.getDocument().getNodeRef());
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.ui.common.component.UIListItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Wizard bean used for creating and editing topic spaces
*
* @author gavinc
*/
public class NewTopicWizard extends NewSpaceWizard
{
public static final String TOPIC_ICON_DEFAULT = "topic_large";
private static final Log logger = LogFactory.getLog(NewTopicWizard.class);
protected String message;
protected String topicType;
protected List<UIListItem> topicIcons;
protected List<SelectItem> topicTypes;
protected ContentService contentService;
/**
* Returns a list of topic types for the user to select from
*
* @return The topic types
*/
public List<SelectItem> getTopicTypes()
{
if (this.topicTypes == null)
{
this.topicTypes = new ArrayList<SelectItem>(3);
// TODO: change this to be based on categories
this.topicTypes.add(new SelectItem("1", "Announcement"));
this.topicTypes.add(new SelectItem("0", "Normal"));
this.topicTypes.add(new SelectItem("2", "Sticky"));
}
return this.topicTypes;
}
/**
* Returns the type of the topic
*
* @return The type of topic
*/
public String getTopicType()
{
return this.topicType;
}
/**
* Sets the type of the topic
*
* @param topicType The type of the topic
*/
public void setTopicType(String topicType)
{
this.topicType = topicType;
}
/**
* Returns the message entered by the user for the first post
*
* @return The message for the first post
*/
public String getMessage()
{
return this.message;
}
/**
* Sets the message
*
* @param message The message
*/
public void setMessage(String message)
{
this.message = message;
}
/**
* @param contentService The contentService to set.
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#init()
*/
public void init()
{
super.init();
this.spaceType = ForumModel.TYPE_TOPIC.toString();
this.icon = TOPIC_ICON_DEFAULT;
this.topicType = "0";
this.message = null;
}
/**
* Returns a list of icons to allow the user to select from.
*
* @return A list of icons
*/
@SuppressWarnings("unchecked")
public List<UIListItem> getIcons()
{
// return the various forum icons
if (this.topicIcons == null)
{
this.topicIcons = new ArrayList<UIListItem>(2);
UIListItem item = new UIListItem();
item.setValue(TOPIC_ICON_DEFAULT);
item.getAttributes().put("image", "/images/icons/topic_large.gif");
this.topicIcons.add(item);
}
return this.topicIcons;
}
/**
* @see org.alfresco.web.bean.wizard.NewSpaceWizard#performCustomProcessing(javax.faces.context.FacesContext)
*/
@Override
protected void performCustomProcessing(FacesContext context)
{
if (this.editMode == false)
{
// *************************
// TODO: Add or update the ForumModel.PROP_TYPE property depending on the editMode
// *************************
// get the node ref of the node that will contain the content
NodeRef containerNodeRef = this.createdNode;
// create a unique file name for the message content
String fileName = GUID.generate() + ".txt";
// create properties for content type
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>(5, 1.0f);
contentProps.put(ContentModel.PROP_NAME, fileName);
// create the node to represent the content
String assocName = QName.createValidLocalName(fileName);
ChildAssociationRef assocRef = this.nodeService.createNode(
containerNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, assocName),
Repository.resolveToQName(ForumModel.TYPE_POST.toString()),
contentProps);
NodeRef postNodeRef = assocRef.getChildRef();
if (logger.isDebugEnabled())
logger.debug("Created post node with filename: " + fileName);
// apply the titled aspect - title and description
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>(3, 1.0f);
titledProps.put(ContentModel.PROP_TITLE, fileName);
this.nodeService.addAspect(postNodeRef, ContentModel.ASPECT_TITLED, titledProps);
if (logger.isDebugEnabled())
logger.debug("Added titled aspect with properties: " + titledProps);
Map<QName, Serializable> editProps = new HashMap<QName, Serializable>(1, 1.0f);
editProps.put(ContentModel.PROP_EDITINLINE, true);
this.nodeService.addAspect(postNodeRef, ContentModel.ASPECT_INLINEEDITABLE, editProps);
if (logger.isDebugEnabled())
logger.debug("Added inlineeditable aspect with properties: " + editProps);
// get a writer for the content and put the file
ContentWriter writer = contentService.getWriter(postNodeRef, ContentModel.PROP_CONTENT, true);
// set the mimetype and encoding
writer.setMimetype(Repository.getMimeTypeForFileName(context, fileName));
writer.setEncoding("UTF-8");
writer.putContent(this.message);
}
}
}

View File

@@ -0,0 +1,981 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.bean.wizard;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.validator.ValidatorException;
import javax.transaction.UserTransaction;
import org.alfresco.config.ConfigService;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.OwnableService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.ContextListener;
import org.alfresco.web.app.context.UIContextService;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.users.UsersBean;
import org.alfresco.web.config.ClientConfigElement;
import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.component.UIActionLink;
import org.apache.log4j.Logger;
/**
* @author Kevin Roast
*/
public class NewUserWizard extends AbstractWizardBean
{
private static Logger logger = Logger.getLogger(NewUserWizard.class);
private static final String WIZARD_TITLE_NEW_ID = "new_user_title";
private static final String WIZARD_DESC_NEW_ID = "new_user_desc";
private static final String WIZARD_TITLE_EDIT_ID = "new_user_title_edit";
private static final String WIZARD_DESC_EDIT_ID = "new_user_desc_edit";
private static final String STEP1_TITLE_ID = "new_user_step1_title";
private static final String STEP1_DESCRIPTION_ID = "new_user_step1_desc";
private static final String STEP2_TITLE_ID = "new_user_step2_title";
private static final String STEP2_DESCRIPTION_ID = "new_user_step2_desc";
private static final String FINISH_INSTRUCTION_ID = "new_user_finish_instruction";
private static final String ERROR = "error_person";
/** form variables */
private String firstName = null;
private String lastName = null;
private String userName = null;
private String password = null;
private String confirm = null;
private String email = null;
private String companyId = null;
private String homeSpaceName = "";
private NodeRef homeSpaceLocation = null;
/** AuthenticationService bean reference */
private AuthenticationService authenticationService;
/** NamespaceService bean reference */
private NamespaceService namespaceService;
/** PermissionService bean reference */
private PermissionService permissionService;
/** PersonService bean reference */
private PersonService personService;
/** OwnableService bean reference */
private OwnableService ownableService;
/** ConfigService bean reference */
private ConfigService configService;
/** action context */
private Node person = null;
/** ref to system people folder */
private NodeRef peopleRef = null;
/** ref to the company home space folder */
private NodeRef companyHomeSpaceRef = null;
/**
* @param authenticationService The AuthenticationService to set.
*/
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
/**
* @param namespaceService The namespaceService to set.
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param permissionService The PermissionService to set.
*/
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
/**
* @param personService The person service.
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* @param ownableService The ownableService to set.
*/
public void setOwnableService(OwnableService ownableService)
{
this.ownableService = ownableService;
}
/**
* @param configService The ConfigService to set.
*/
public void setConfigService(ConfigService configService)
{
this.configService = configService;
}
/**
* Initialises the wizard
*/
public void init()
{
super.init();
// reset all variables
this.firstName = "";
this.lastName = "";
this.userName = "";
this.password = "";
this.confirm = "";
this.email = "";
this.companyId = "";
this.homeSpaceName = "";
this.homeSpaceLocation = getCompanyHomeSpace();
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#populate()
*/
public void populate()
{
// set values for edit mode
Map<String, Object> props = getPerson().getProperties();
this.firstName = (String) props.get("firstName");
this.lastName = (String) props.get("lastName");
this.userName = (String) props.get("userName");
this.password = "";
this.confirm = "";
this.email = (String) props.get("email");
this.companyId = (String) props.get("organizationId");
// calculate home space name and parent space Id from homeFolderId
this.homeSpaceLocation = null; // default to Company root space
this.homeSpaceName = ""; // default to none set below root
NodeRef homeFolderRef = (NodeRef) props.get("homeFolder");
if (this.nodeService.exists(homeFolderRef) == true)
{
ChildAssociationRef childAssocRef = this.nodeService.getPrimaryParent(homeFolderRef);
NodeRef parentRef = childAssocRef.getParentRef();
if (this.nodeService.getRootNode(Repository.getStoreRef()).equals(parentRef) == false)
{
this.homeSpaceLocation = parentRef;
this.homeSpaceName = Repository.getNameForNode(nodeService, homeFolderRef);
}
else
{
this.homeSpaceLocation = homeFolderRef;
}
}
if (logger.isDebugEnabled())
logger.debug("Edit user home space location: " + homeSpaceLocation + " home space name: " + homeSpaceName);
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardDescription()
*/
public String getWizardDescription()
{
if (this.editMode)
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_DESC_EDIT_ID);
}
else
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_DESC_NEW_ID);
}
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getWizardTitle()
*/
public String getWizardTitle()
{
if (this.editMode)
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_TITLE_EDIT_ID);
}
else
{
return Application.getMessage(FacesContext.getCurrentInstance(), WIZARD_TITLE_NEW_ID);
}
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepTitle()
*/
public String getStepTitle()
{
String stepTitle = null;
switch (this.currentStep)
{
case 1:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_TITLE_ID);
break;
}
case 2:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_TITLE_ID);
break;
}
case 3:
{
stepTitle = Application.getMessage(FacesContext.getCurrentInstance(), SUMMARY_TITLE_ID);
break;
}
default:
{
stepTitle = "";
}
}
return stepTitle;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepDescription()
*/
public String getStepDescription()
{
String stepDesc = null;
switch (this.currentStep)
{
case 1:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP1_DESCRIPTION_ID);
break;
}
case 2:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), STEP2_DESCRIPTION_ID);
break;
}
case 3:
{
stepDesc = Application.getMessage(FacesContext.getCurrentInstance(), SUMMARY_DESCRIPTION_ID);
break;
}
default:
{
stepDesc = "";
}
}
return stepDesc;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#getStepInstructions()
*/
public String getStepInstructions()
{
String stepInstruction = null;
switch (this.currentStep)
{
case 3:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), FINISH_INSTRUCTION_ID);
break;
}
default:
{
stepInstruction = Application.getMessage(FacesContext.getCurrentInstance(), DEFAULT_INSTRUCTION_ID);
}
}
return stepInstruction;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#determineOutcomeForStep(int)
*/
protected String determineOutcomeForStep(int step)
{
String outcome = null;
switch (step)
{
case 1:
{
outcome = "person-properties";
break;
}
case 2:
{
outcome = "user-properties";
break;
}
case 3:
{
outcome = "summary";
break;
}
default:
{
outcome = CANCEL_OUTCOME;
}
}
return outcome;
}
/**
* @see org.alfresco.web.bean.wizard.AbstractWizardBean#finish()
*/
public String finish()
{
String outcome = FINISH_OUTCOME;
// TODO: implement create new Person object from specified details
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
if (this.editMode)
{
// update the existing node in the repository
NodeRef nodeRef = getPerson().getNodeRef();
Map<QName, Serializable> props = this.nodeService.getProperties(nodeRef);
props.put(ContentModel.PROP_USERNAME, this.userName);
props.put(ContentModel.PROP_FIRSTNAME, this.firstName);
props.put(ContentModel.PROP_LASTNAME, this.lastName);
// calculate whether we need to move the old home space or create new
NodeRef newHomeFolderRef;
NodeRef oldHomeFolderRef = (NodeRef)this.nodeService.getProperty(nodeRef, ContentModel.PROP_HOMEFOLDER);
boolean moveHomeSpace = false;
boolean renameHomeSpace = false;
if (oldHomeFolderRef != null && this.nodeService.exists(oldHomeFolderRef) == true)
{
// the original home folder ref exists so may need moving if it has been changed
ChildAssociationRef childAssocRef = this.nodeService.getPrimaryParent(oldHomeFolderRef);
NodeRef currentHomeSpaceLocation = childAssocRef.getParentRef();
if (this.homeSpaceName.length() != 0)
{
if (currentHomeSpaceLocation.equals(this.homeSpaceLocation) == false &&
oldHomeFolderRef.equals(this.homeSpaceLocation) == false &&
currentHomeSpaceLocation.equals(getCompanyHomeSpace()) == false)
{
moveHomeSpace = true;
}
String oldHomeSpaceName = Repository.getNameForNode(nodeService, oldHomeFolderRef);
if (oldHomeSpaceName.equals(this.homeSpaceName) == false &&
oldHomeFolderRef.equals(this.homeSpaceLocation) == false)
{
renameHomeSpace = true;
}
}
}
if (logger.isDebugEnabled())
logger.debug("Moving space: " + moveHomeSpace + " and renaming space: " + renameHomeSpace);
if (moveHomeSpace == false && renameHomeSpace == false)
{
if (this.homeSpaceLocation != null && this.homeSpaceName.length() != 0)
{
newHomeFolderRef = createHomeSpace(this.homeSpaceLocation.getId(), this.homeSpaceName, false);
}
else if (this.homeSpaceLocation != null)
{
// location selected but no home space name entered,
// so the home ref should be set to the newly selected space
newHomeFolderRef = this.homeSpaceLocation;
// set the permissions for this space so the user can access it
}
else
{
// nothing selected - use Company Home by default
newHomeFolderRef = getCompanyHomeSpace();
}
}
else
{
// either move, rename or both required
if (moveHomeSpace == true)
{
this.nodeService.moveNode(
oldHomeFolderRef,
this.homeSpaceLocation,
ContentModel.ASSOC_CONTAINS,
this.nodeService.getPrimaryParent(oldHomeFolderRef).getQName());
}
newHomeFolderRef = oldHomeFolderRef; // ref ID doesn't change
if (renameHomeSpace == true)
{
// change HomeSpace node name
this.nodeService.setProperty(newHomeFolderRef, ContentModel.PROP_NAME, this.homeSpaceName);
}
}
props.put(ContentModel.PROP_HOMEFOLDER, newHomeFolderRef);
props.put(ContentModel.PROP_EMAIL, this.email);
props.put(ContentModel.PROP_ORGID, this.companyId);
this.nodeService.setProperties(nodeRef, props);
// TODO: RESET HomeSpace Ref found in top-level navigation bar!
// NOTE: not need cos only admin can do this?
}
else
{
if (this.password.equals(this.confirm))
{
if (!this.personService.getUserNamesAreCaseSensitive())
{
this.userName = this.userName.toLowerCase();
}
// create properties for Person type from submitted Form data
Map<QName, Serializable> props = new HashMap<QName, Serializable>(7, 1.0f);
props.put(ContentModel.PROP_USERNAME, this.userName);
props.put(ContentModel.PROP_FIRSTNAME, this.firstName);
props.put(ContentModel.PROP_LASTNAME, this.lastName);
NodeRef homeSpaceNodeRef;
if (this.homeSpaceLocation != null && this.homeSpaceName.length() != 0)
{
// create new
homeSpaceNodeRef = createHomeSpace(this.homeSpaceLocation.getId(), this.homeSpaceName, true);
}
else if (this.homeSpaceLocation != null)
{
// set to existing
homeSpaceNodeRef = homeSpaceLocation;
setupHomeSpacePermissions(homeSpaceNodeRef);
}
else
{
// default to Company Home
homeSpaceNodeRef = getCompanyHomeSpace();
}
props.put(ContentModel.PROP_HOMEFOLDER, homeSpaceNodeRef);
props.put(ContentModel.PROP_EMAIL, this.email);
props.put(ContentModel.PROP_ORGID, this.companyId);
// create the node to represent the Person
String assocName = QName.createValidLocalName(this.userName);
NodeRef newPerson = this.personService.createPerson(props);
// ensure the user can access their own Person object
this.permissionService.setPermission(newPerson, this.userName, permissionService.getAllPermission(), true);
if (logger.isDebugEnabled()) logger.debug("Created Person node for username: " + this.userName);
// create the ACEGI Authentication instance for the new user
this.authenticationService.createAuthentication(this.userName, this.password.toCharArray());
if (logger.isDebugEnabled()) logger.debug("Created User Authentication instance for username: " + this.userName);
}
else
{
outcome = null;
Utils.addErrorMessage(Application.getMessage(context, UsersBean.ERROR_PASSWORD_MATCH));
}
}
// commit the transaction
tx.commit();
// reset the richlist component so it rebinds to the users list
invalidateUserList();
}
catch (Exception e)
{
// rollback the transaction
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext.getCurrentInstance(), ERROR), e
.getMessage()), e);
outcome = null;
}
return outcome;
}
/**
* @return Returns the summary data for the wizard.
*/
public String getSummary()
{
String homeSpaceLabel = this.homeSpaceName;
if (this.homeSpaceName.length() == 0 && this.homeSpaceLocation != null)
{
homeSpaceLabel = Repository.getNameForNode(this.nodeService, this.homeSpaceLocation);
}
ResourceBundle bundle = Application.getBundle(FacesContext.getCurrentInstance());
return buildSummary(new String[] { bundle.getString("name"), bundle.getString("username"),
bundle.getString("password"), bundle.getString("homespace") }, new String[] {
this.firstName + " " + this.lastName, this.userName, "********", homeSpaceLabel });
}
/**
* Init the users screen
*/
public void setupUsers(ActionEvent event)
{
invalidateUserList();
}
/**
* Action listener called when the wizard is being launched for editing an
* existing node.
*/
public void startWizardForEdit(ActionEvent event)
{
UIActionLink link = (UIActionLink) event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
try
{
// create the node ref, then our node representation
NodeRef ref = new NodeRef(Repository.getStoreRef(), id);
Node node = new Node(ref);
// remember the Person node
setPerson(node);
// set the wizard in edit mode
this.editMode = true;
// populate the wizard's default values with the current value
// from the node being edited
init();
populate();
// clear the UI state in preparation for finishing the action
// and returning to the main page
invalidateUserList();
if (logger.isDebugEnabled()) logger.debug("Started wizard : " + getWizardTitle() + " for editing");
}
catch (InvalidNodeRefException refErr)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(FacesContext.getCurrentInstance(),
Repository.ERROR_NODEREF), new Object[] { id }));
}
}
else
{
setPerson(null);
}
}
/**
* @return Returns the companyId.
*/
public String getCompanyId()
{
return this.companyId;
}
/**
* @param companyId
* The companyId to set.
*/
public void setCompanyId(String companyId)
{
this.companyId = companyId;
}
/**
* @return Returns the email.
*/
public String getEmail()
{
return this.email;
}
/**
* @param email
* The email to set.
*/
public void setEmail(String email)
{
this.email = email;
}
/**
* @return Returns the firstName.
*/
public String getFirstName()
{
return this.firstName;
}
/**
* @param firstName The firstName to set.
*/
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
/**
* @return Returns the homeSpaceLocation.
*/
public NodeRef getHomeSpaceLocation()
{
return this.homeSpaceLocation;
}
/**
* @param homeSpaceLocation The homeSpaceLocation to set.
*/
public void setHomeSpaceLocation(NodeRef homeSpaceLocation)
{
this.homeSpaceLocation = homeSpaceLocation;
}
/**
* @return Returns the homeSpaceName.
*/
public String getHomeSpaceName()
{
return this.homeSpaceName;
}
/**
* @param homeSpaceName The homeSpaceName to set.
*/
public void setHomeSpaceName(String homeSpaceName)
{
this.homeSpaceName = homeSpaceName;
}
/**
* @return Returns the lastName.
*/
public String getLastName()
{
return this.lastName;
}
/**
* @param lastName The lastName to set.
*/
public void setLastName(String lastName)
{
this.lastName = lastName;
}
/**
* @return Returns the userName.
*/
public String getUserName()
{
return this.userName;
}
/**
* @param userName The userName to set.
*/
public void setUserName(String userName)
{
this.userName = userName;
}
/**
* @return Returns the password.
*/
public String getPassword()
{
return this.password;
}
/**
* @param password The password to set.
*/
public void setPassword(String password)
{
this.password = password;
}
/**
* @return Returns the confirm password.
*/
public String getConfirm()
{
return this.confirm;
}
/**
* @param confirm The confirm password to set.
*/
public void setConfirm(String confirm)
{
this.confirm = confirm;
}
/**
* @return Returns the person context.
*/
public Node getPerson()
{
return this.person;
}
/**
* @param person The person context to set.
*/
public void setPerson(Node person)
{
this.person = person;
}
public boolean getEditMode()
{
return this.editMode;
}
// ------------------------------------------------------------------------------
// Validator methods
/**
* Validate password field data is acceptable
*/
public void validatePassword(FacesContext context, UIComponent component, Object value) throws ValidatorException
{
String pass = (String) value;
if (pass.length() < 5 || pass.length() > 12)
{
String err = "Password must be between 5 and 12 characters in length.";
throw new ValidatorException(new FacesMessage(err));
}
for (int i = 0; i < pass.length(); i++)
{
if (Character.isLetterOrDigit(pass.charAt(i)) == false)
{
String err = "Password can only contain characters or digits.";
throw new ValidatorException(new FacesMessage(err));
}
}
}
/**
* Validate Username field data is acceptable
*/
public void validateUsername(FacesContext context, UIComponent component, Object value) throws ValidatorException
{
String pass = (String) value;
if (pass.length() < 5 || pass.length() > 12)
{
String err = "Username must be between 5 and 12 characters in length.";
throw new ValidatorException(new FacesMessage(err));
}
for (int i = 0; i < pass.length(); i++)
{
if (Character.isLetterOrDigit(pass.charAt(i)) == false)
{
String err = "Username can only contain characters or digits.";
throw new ValidatorException(new FacesMessage(err));
}
}
}
// ------------------------------------------------------------------------------
// Helper methods
/**
* Helper to return the company home space
*
* @return company home space NodeRef
*/
private NodeRef getCompanyHomeSpace()
{
if (this.companyHomeSpaceRef == null)
{
String companyXPath = Application.getRootPath(FacesContext.getCurrentInstance());
NodeRef rootNodeRef = this.nodeService.getRootNode(Repository.getStoreRef());
List<NodeRef> nodes = this.searchService.selectNodes(rootNodeRef, companyXPath, null, this.namespaceService,
false);
if (nodes.size() == 0)
{
throw new IllegalStateException("Unable to find company home space path: " + companyXPath);
}
this.companyHomeSpaceRef = nodes.get(0);
}
return this.companyHomeSpaceRef;
}
/**
* Create the specified home space if it does not exist, and return the ID
*
* @param locationId
* Parent location
* @param spaceName
* Home space to create, can be null to simply return the parent
* @param error
* True to throw an error if the space already exists, else
* ignore and return
*
* @return ID of the home space
*/
private NodeRef createHomeSpace(String locationId, String spaceName, boolean error)
{
String homeSpaceId = locationId;
NodeRef homeSpaceNodeRef = null;
if (spaceName != null && spaceName.length() != 0)
{
NodeRef parentRef = new NodeRef(Repository.getStoreRef(), locationId);
// check for existance of home space with same name - return immediately
// if it exists or throw an exception an give user chance to enter another name
// TODO: this might be better replaced with an XPath query!
List<ChildAssociationRef> children = this.nodeService.getChildAssocs(parentRef);
for (ChildAssociationRef ref : children)
{
String childNodeName = (String) this.nodeService.getProperty(ref.getChildRef(), ContentModel.PROP_NAME);
if (spaceName.equals(childNodeName))
{
if (error)
{
throw new AlfrescoRuntimeException("A Home Space with the same name already exists.");
}
else
{
return ref.getChildRef();
}
}
}
// space does not exist already, create a new Space under it with
// the specified name
String qname = QName.createValidLocalName(spaceName);
ChildAssociationRef assocRef = this.nodeService.createNode(parentRef, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, qname), ContentModel.TYPE_FOLDER);
NodeRef nodeRef = assocRef.getChildRef();
// set the name property on the node
this.nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, spaceName);
if (logger.isDebugEnabled()) logger.debug("Created Home Space for with name: " + spaceName);
// apply the uifacets aspect - icon, title and description props
Map<QName, Serializable> uiFacetsProps = new HashMap<QName, Serializable>(3);
uiFacetsProps.put(ContentModel.PROP_ICON, NewSpaceWizard.SPACE_ICON_DEFAULT);
uiFacetsProps.put(ContentModel.PROP_TITLE, spaceName);
this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_UIFACETS, uiFacetsProps);
setupHomeSpacePermissions(nodeRef);
// return the ID of the created space
homeSpaceNodeRef = nodeRef;
homeSpaceId = nodeRef.getId();
}
return homeSpaceNodeRef;
}
/**
* Setup the default permissions for this and other users on the Home Space
*
* @param homeSpaceRef Home Space reference
*/
private void setupHomeSpacePermissions(NodeRef homeSpaceRef)
{
// Admin Authority has full permissions by default (automatic - set in the permission config)
// give full permissions to the new user
this.permissionService.setPermission(homeSpaceRef, this.userName, permissionService.getAllPermission(), true);
// by default other users will only have GUEST access to the space contents
// or whatever is configured as the default in the web-client-xml config
String permission = getDefaultPermission();
if (permission != null && permission.length() != 0)
{
this.permissionService.setPermission(homeSpaceRef, permissionService.getAllAuthorities(), permission, true);
}
// the new user is the OWNER of their own space and always has full permissions
this.ownableService.setOwner(homeSpaceRef, this.userName);
this.permissionService.setPermission(homeSpaceRef, permissionService.getOwnerAuthority(), permissionService.getAllPermission(), true);
// now detach (if we did this first we could not set any permissions!)
this.permissionService.setInheritParentPermissions(homeSpaceRef, false);
}
/**
* @return default permission string to set for other users for a new Home Space
*/
private String getDefaultPermission()
{
ClientConfigElement config = (ClientConfigElement)this.configService.getGlobalConfig().getConfigElement(
ClientConfigElement.CONFIG_ELEMENT_ID);
return config.getHomeSpacePermission();
}
private void invalidateUserList()
{
UIContextService.getInstance(FacesContext.getCurrentInstance()).notifyBeans();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.Set;
import org.alfresco.config.evaluator.Evaluator;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
/**
* Evaluator that determines whether a given object has a particular aspect applied
*
* @author gavinc
*/
public class AspectEvaluator implements Evaluator
{
/**
* Determines whether the given aspect is applied to the given object
*
* @see org.alfresco.config.evaluator.Evaluator#applies(java.lang.Object, java.lang.String)
*/
public boolean applies(Object obj, String condition)
{
boolean result = false;
if (obj instanceof Node)
{
Set aspects = ((Node)obj).getAspects();
if (aspects != null)
{
QName spaceQName = Repository.resolveToQName(condition);
result = aspects.contains(spaceQName);
}
}
return result;
}
}

View File

@@ -0,0 +1,403 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.element.ConfigElementAdapter;
/**
* Custom config element that represents config values for the client
*
* @author Kevin Roast
*/
public class ClientConfigElement extends ConfigElementAdapter
{
public static final String CONFIG_ELEMENT_ID = "client";
public static final String VIEW_DETAILS = "details";
public static final String VIEW_ICONS = "icons";
public static final String VIEW_LIST = "list";
public static final String VIEW_BUBBLE = "bubble";
private static final String SEPARATOR = ":";
// defaults for any config values not supplied
private int defaultPageSize = 10;
private String defaultView = "details";
private String defaultSortColumn = "name";
private String defaultSortOrder = "ascending";
// list to store all the configured views
private List<String> views = new ArrayList<String>(4);
// map to store all the default views
private Map<String, String> defaultViews = new HashMap<String, String>(4);
// map to store all default pages sizes for configured client views
private Map<String, Integer> pagesSizes = new HashMap<String, Integer>(10);
// map to store default sort columns for configured views
private Map<String, String> sortColumns = new HashMap<String, String>(4);
// list of pages that have been configured to have ascending sorts
private List<String> descendingSorts = new ArrayList<String>(1);
private int recentSpacesItems = 6;
private int searchMinimum = 3;
private String helpUrl = null;
private String editLinkType = null;
private Map<String, String> localeMap = new HashMap<String, String>();
private List<String> languages = new ArrayList<String>(8);
private String homeSpacePermission = null;
private List<String> contentTypes = null;
private List<CustomProperty> customProps = null;
/**
* Default Constructor
*/
public ClientConfigElement()
{
super(CONFIG_ELEMENT_ID);
// add the default page sizes to the map
this.pagesSizes.put(VIEW_DETAILS, defaultPageSize);
this.pagesSizes.put(VIEW_LIST, defaultPageSize);
this.pagesSizes.put(VIEW_ICONS, 9);
this.pagesSizes.put(VIEW_BUBBLE, 5);
}
/**
* Constructor
*
* @param name Name of the element this config element represents
*/
public ClientConfigElement(String name)
{
super(name);
}
/**
* @see org.alfresco.config.element.ConfigElementAdapter#combine(org.alfresco.config.ConfigElement)
*/
public ConfigElement combine(ConfigElement configElement)
{
return null;
}
/**
* Adds a configured view
*
* @param renderer The implementation class of the view (the renderer)
*/
public void addView(String renderer)
{
this.views.add(renderer);
}
/**
* Returns a map of configured views for the client
*
* @return List of the implementation classes for the configured views
*/
public List<String> getViews()
{
return this.views;
}
/**
* Adds a default view setting
*
* @param page The page to set the default view for
* @param view The view name that will be the default
*/
public void addDefaultView(String page, String view)
{
this.defaultViews.put(page, view);
}
/**
* Returns the default view for the given page
*
* @param page The page to get the default view for
* @return The defualt view, if there isn't a configured default for the
* given page 'details' will be returned
*/
public String getDefaultView(String page)
{
String view = this.defaultViews.get(page);
if (view == null)
{
view = this.defaultView;
}
return view;
}
/**
* Adds a configured page size to the internal store
*
* @param page The name of the page i.e. browse, forums etc.
* @param view The name of the view the size is for i.e. details, icons etc.
* @param size The size of the page
*/
public void addDefaultPageSize(String page, String view, int size)
{
this.pagesSizes.put(page + SEPARATOR + view, new Integer(size));
}
/**
* Returns the page size for the given page and view combination
*
* @param page The name of the page i.e. browse, forums etc.
* @param view The name of the view the size is for i.e. details, icons etc.
* @return The size of the requested page, if the combination doesn't exist
* the default for the view will be used, if the view doesn't exist either
* 10 will be returned.
*/
public int getDefaultPageSize(String page, String view)
{
Integer pageSize = this.pagesSizes.get(page + SEPARATOR + view);
// try just the view if the combination isn't present
if (pageSize == null)
{
pageSize = this.pagesSizes.get(view);
// if the view is not present either default to 10
if (pageSize == null)
{
pageSize = new Integer(10);
}
}
return pageSize.intValue();
}
/**
* Adds a default sorting column for the given page
*
* @param page The name of the page i.e. browse, forums etc.
* @param column The name of the column to initially sort by
*/
public void addDefaultSortColumn(String page, String column)
{
this.sortColumns.put(page, column);
}
/**
* Returns the default sort column for the given page
*
* @param page The name of the page i.e. browse, forums etc.
* @return The name of the column to sort by, name is returned if
* the page is not found
*/
public String getDefaultSortColumn(String page)
{
String column = this.sortColumns.get(page);
if (column == null)
{
column = this.defaultSortColumn;
}
return column;
}
/**
* Sets the given page as using descending sorts
*
* @param page The name of the page i.e. browse, forums etc.
*/
public void addDescendingSort(String page)
{
this.descendingSorts.add(page);
}
/**
* Determines whether the given page has been
* configured to use descending sorting by default
*
* @param page The name of the page i.e. browse, forums etc.
* @return true if the page should use descending sorts
*/
public boolean hasDescendingSort(String page)
{
return this.descendingSorts.contains(page);
}
/**
* @return Returns the recentSpacesItems.
*/
public int getRecentSpacesItems()
{
return this.recentSpacesItems;
}
/**
* @param recentSpacesItems The recentSpacesItems to set.
*/
/*package*/ void setRecentSpacesItems(int recentSpacesItems)
{
this.recentSpacesItems = recentSpacesItems;
}
/**
* Add a language locale and display label to the list.
*
* @param locale Locale code
* @param label Display label
*/
/*package*/ void addLanguage(String locale, String label)
{
this.localeMap.put(locale, label);
this.languages.add(locale);
}
/**
* @return List of supported language locale strings in config file order
*/
public List<String> getLanguages()
{
return this.languages;
}
/**
* @param locale The locale string to lookup language label for
*
* @return the language label for specified locale string, or null if not found
*/
public String getLabelForLanguage(String locale)
{
return this.localeMap.get(locale);
}
/**
* @return Returns the help Url.
*/
public String getHelpUrl()
{
return this.helpUrl;
}
/**
* @param helpUrl The help Url to set.
*/
/*package*/ void setHelpUrl(String helpUrl)
{
this.helpUrl = helpUrl;
}
/**
* @return Returns the edit link type.
*/
public String getEditLinkType()
{
return this.editLinkType;
}
/**
* @param editLinkType The edit link type to set.
*/
/*package*/ void setEditLinkType(String editLinkType)
{
this.editLinkType = editLinkType;
}
/**
* @return Returns the search minimum number of characters.
*/
public int getSearchMinimum()
{
return this.searchMinimum;
}
/**
* @param searchMinimum The searchMinimum to set.
*/
/*package*/ void setSearchMinimum(int searchMinimum)
{
this.searchMinimum = searchMinimum;
}
/**
* @return Returns the default Home Space permissions.
*/
public String getHomeSpacePermission()
{
return this.homeSpacePermission;
}
/**
* @param homeSpacePermission The default Home Space permission to set.
*/
/*package*/ void setHomeSpacePermission(String homeSpacePermission)
{
this.homeSpacePermission = homeSpacePermission;
}
/**
* @return Returns the contentTypes.
*/
public List<String> getContentTypes()
{
return this.contentTypes;
}
/**
* @param contentTypes The contentTypes to set.
*/
/*package*/ void setContentTypes(List<String> contentTypes)
{
this.contentTypes = contentTypes;
}
/**
* @return Returns the customProps.
*/
public List<CustomProperty> getCustomProperties()
{
return this.customProps;
}
/**
* @param customProps The customProps to set.
*/
/*package*/ void setCustomProperties(List<CustomProperty> customProps)
{
this.customProps = customProps;
}
public static class CustomProperty
{
CustomProperty(String type, String aspect, String property)
{
Type = type;
Aspect = aspect;
Property = property;
}
public String Type;
public String Aspect;
public String Property;
}
}

View File

@@ -0,0 +1,267 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigException;
import org.alfresco.config.xml.elementreader.ConfigElementReader;
import org.alfresco.web.config.ClientConfigElement.CustomProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;
/**
* Custom element reader to parse config for client config values
*
* @author Kevin Roast
*/
public class ClientElementReader implements ConfigElementReader
{
public static final String ELEMENT_VIEWS = "views";
public static final String ELEMENT_VIEW = "view";
public static final String ELEMENT_VIEWDEFAULTS = "view-defaults";
public static final String ELEMENT_PAGESIZE = "page-size";
public static final String ELEMENT_SORTCOLUMN = "sort-column";
public static final String ELEMENT_SORTDESCENDING = "sort-descending";
public static final String ELEMENT_RECENTSPACESITEMS = "recent-spaces-items";
public static final String ELEMENT_LANGUAGES = "languages";
public static final String ELEMENT_LANGUAGE = "language";
public static final String ATTRIBUTE_LOCALE = "locale";
public static final String ATTRIBUTE_NAME = "name";
public static final String ELEMENT_HELPURL = "help-url";
public static final String ELEMENT_EDITLINKTYPE = "edit-link-type";
public static final String ELEMENT_SEARCHMINIMUM = "search-minimum";
public static final String ELEMENT_HOMESPACEPERMISSION = "home-space-permission";
public static final String ELEMENT_ADVANCEDSEARCH = "advanced-search";
public static final String ELEMENT_CONTENTTYPES = "content-types";
public static final String ELEMENT_TYPE = "type";
public static final String ELEMENT_CUSTOMPROPS = "custom-properties";
public static final String ELEMENT_METADATA = "meta-data";
public static final String ATTRIBUTE_TYPE = "type";
public static final String ATTRIBUTE_PROPERTY = "property";
public static final String ATTRIBUTE_ASPECT = "aspect";
private static Log logger = LogFactory.getLog(ClientElementReader.class);
/**
* @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
*/
@SuppressWarnings("unchecked")
public ConfigElement parse(Element element)
{
ClientConfigElement configElement = null;
if (element != null)
{
String name = element.getName();
if (name.equals(ClientConfigElement.CONFIG_ELEMENT_ID) == false)
{
throw new ConfigException("ClientElementReader can only parse " +
ClientConfigElement.CONFIG_ELEMENT_ID + "elements, the element passed was '" +
name + "'");
}
configElement = new ClientConfigElement();
// get the configured views
Element views = element.element(ELEMENT_VIEWS);
if (views != null)
{
Iterator<Element> renderers = views.elementIterator(ELEMENT_VIEW);
while (renderers.hasNext())
{
Element renderer = renderers.next();
configElement.addView(renderer.getTextTrim());
}
}
// get all the view related default settings
Element viewDefaults = element.element(ELEMENT_VIEWDEFAULTS);
if (viewDefaults != null)
{
Iterator<Element> pages = viewDefaults.elementIterator();
while (pages.hasNext())
{
Element page = pages.next();
String pageName = page.getName();
// get the default view mode for the page
Element defaultView = page.element(ELEMENT_VIEW);
if (defaultView != null)
{
String viewName = defaultView.getTextTrim();
configElement.addDefaultView(pageName, viewName);
}
// get the initial sort column
Element sortColumn = page.element(ELEMENT_SORTCOLUMN);
if (sortColumn != null)
{
String column = sortColumn.getTextTrim();
configElement.addDefaultSortColumn(pageName, column);
}
// get the sort descending option
Element sortDesc = page.element(ELEMENT_SORTDESCENDING);
if (sortDesc != null)
{
Boolean descending = new Boolean(sortDesc.getTextTrim());
if (descending.booleanValue() == true)
{
configElement.addDescendingSort(pageName);
}
}
// process the page-size element
processPageSizeElement(page.element(ELEMENT_PAGESIZE),
pageName, configElement);
}
}
// get the languages sub-element
Element languages = element.element(ELEMENT_LANGUAGES);
if (languages != null)
{
Iterator<Element> langsItr = languages.elementIterator(ELEMENT_LANGUAGE);
while (langsItr.hasNext())
{
Element language = langsItr.next();
String localeCode = language.attributeValue(ATTRIBUTE_LOCALE);
String label = language.getTextTrim();
if (localeCode != null && localeCode.length() != 0 &&
label != null && label.length() != 0)
{
// store the language code against the display label
configElement.addLanguage(localeCode, label);
}
}
}
// get the recent space max items
Element recentSpaces = element.element(ELEMENT_RECENTSPACESITEMS);
if (recentSpaces != null)
{
configElement.setRecentSpacesItems(Integer.parseInt(recentSpaces.getTextTrim()));
}
// get the Help url
Element helpUrl = element.element(ELEMENT_HELPURL);
if (helpUrl != null)
{
configElement.setHelpUrl(helpUrl.getTextTrim());
}
// get the edit link type
Element editLinkType = element.element(ELEMENT_EDITLINKTYPE);
if (editLinkType != null)
{
configElement.setEditLinkType(editLinkType.getTextTrim());
}
// get the minimum number of characters for valid search string
Element searchMin = element.element(ELEMENT_SEARCHMINIMUM);
if (searchMin != null)
{
configElement.setSearchMinimum(Integer.parseInt(searchMin.getTextTrim()));
}
// get the default permission for newly created users Home Spaces
Element permission = element.element(ELEMENT_HOMESPACEPERMISSION);
if (permission != null)
{
configElement.setHomeSpacePermission(permission.getTextTrim());
}
// get the Advanced Search config block
Element advsearch = element.element(ELEMENT_ADVANCEDSEARCH);
if (advsearch != null)
{
// get the list of content types
Element contentTypes = advsearch.element(ELEMENT_CONTENTTYPES);
Iterator<Element> typesItr = contentTypes.elementIterator(ELEMENT_TYPE);
List<String> types = new ArrayList<String>(5);
while (typesItr.hasNext())
{
Element contentType = typesItr.next();
String type = contentType.attributeValue(ATTRIBUTE_NAME);
if (type != null)
{
types.add(type);
}
}
configElement.setContentTypes(types);
// get the list of custom properties to display
Element customProps = advsearch.element(ELEMENT_CUSTOMPROPS);
Iterator<Element> propsItr = customProps.elementIterator(ELEMENT_METADATA);
List<CustomProperty> props = new ArrayList<CustomProperty>(5);
while (propsItr.hasNext())
{
Element propElement = propsItr.next();
String type = propElement.attributeValue(ATTRIBUTE_TYPE);
String aspect = propElement.attributeValue(ATTRIBUTE_ASPECT);
String prop = propElement.attributeValue(ATTRIBUTE_PROPERTY);
props.add(new ClientConfigElement.CustomProperty(type, aspect, prop));
}
configElement.setCustomProperties(props);
}
}
return configElement;
}
/**
* Processes a page-size element
*
* @param pageSizeElement The element to process
* @param page The page the page-size element belongs to
* @param configElement The config element being populated
*/
@SuppressWarnings("unchecked")
private void processPageSizeElement(Element pageSizeElement, String page,
ClientConfigElement configElement)
{
if (pageSizeElement != null)
{
Iterator<Element> views = pageSizeElement.elementIterator();
while (views.hasNext())
{
Element view = views.next();
String viewName = view.getName();
String pageSize = view.getTextTrim();
try
{
configElement.addDefaultPageSize(page, viewName, Integer.parseInt(pageSize));
}
catch (NumberFormatException nfe)
{
if (logger.isWarnEnabled())
{
logger.warn("Failed to set page size for view '" + viewName +
"' in page '" + page + "' as '" + pageSize +
"' is an invalid number!");
}
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.element.ConfigElementAdapter;
/**
* @author Kevin Roast
*/
public final class MimeTypeConfigElement extends ConfigElementAdapter
{
/**
* Default Constructor
*/
public MimeTypeConfigElement()
{
super(MimeTypesElementReader.ELEMENT_MIMETYPES);
}
/**
* Constructor
*
* @param mappings Map of mimetype elements to use
*/
public MimeTypeConfigElement(Map<String, String> mappings)
{
super(MimeTypesElementReader.ELEMENT_MIMETYPES);
this.mimetypes = mappings;
}
/**
* @see org.alfresco.config.element.ConfigElementAdapter#combine(org.alfresco.config.ConfigElement)
*/
public ConfigElement combine(ConfigElement configElement)
{
MimeTypeConfigElement combined = new MimeTypeConfigElement(this.mimetypes);
if (configElement instanceof MimeTypeConfigElement)
{
combined.mimetypes.putAll( ((MimeTypeConfigElement)configElement).mimetypes );
}
return combined;
}
/**
* Add a mimetype extension mapping to the config element
*
* @param ext extension to map against
* @param mimetype mimetype content type for the specified extension
*/
public void addMapping(String ext, String mimetype)
{
this.mimetypes.put(ext, mimetype);
}
/**
* Return the mimetype for the specified extension
*
* @param ext File
*
* @return mimetype content type or null if not found
*/
public String getMimeType(String ext)
{
return this.mimetypes.get(ext);
}
private Map<String, String> mimetypes = new HashMap<String, String>(89, 1.0f);
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.Iterator;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigException;
import org.alfresco.config.xml.elementreader.ConfigElementReader;
import org.dom4j.Element;
/**
* @author Kevin Roast
*/
public class MimeTypesElementReader implements ConfigElementReader
{
public final static String ELEMENT_MIMETYPES = "mimetypes";
public final static String ELEMENT_MIMEMAPPING = "mime-mapping";
public final static String ELEMENT_EXTENSION = "extension";
public final static String ELEMENT_MIMETYPE = "mime-type";
/**
* @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
*/
public ConfigElement parse(Element element)
{
MimeTypeConfigElement configElement = null;
if (element != null)
{
String name = element.getName();
if (name.equals(ELEMENT_MIMETYPES) == false)
{
throw new ConfigException("MimeTypesElementReader can only parse " +
ELEMENT_MIMETYPES + "elements, the element passed was '" +
name + "'");
}
configElement = new MimeTypeConfigElement();
// walk the mime-mapping elements
Iterator<Element> mappings = element.elementIterator(ELEMENT_MIMEMAPPING);
while (mappings.hasNext())
{
Element mapping = mappings.next();
Element extensionElement = mapping.element(ELEMENT_EXTENSION);
Element mimetypeElement = mapping.element(ELEMENT_MIMETYPE);
if (extensionElement == null || mimetypeElement == null)
{
throw new ConfigException("mime-mapping element must specify 'extension' and 'mime-type'");
}
String extension = extensionElement.getTextTrim();
String mimetype = mimetypeElement.getTextTrim();
if (extension == null || extension.length() == 0)
{
throw new ConfigException("mime-mapping extension element value must be specified");
}
if (mimetype == null || mimetype.length() == 0)
{
throw new ConfigException("mime-mapping mimetype element value must be specified");
}
// add the mimetype extension to the config element
configElement.addMapping(extension, mimetype);
}
}
return configElement;
}
}

View File

@@ -0,0 +1,246 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.HashMap;
import java.util.List;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.element.ConfigElementAdapter;
import org.alfresco.config.element.GenericConfigElement;
/**
* Custom config element that represents the config data for navigation
*
* @author gavinc
*/
public class NavigationConfigElement extends ConfigElementAdapter
{
private HashMap<String, NavigationResult> viewIds = new HashMap<String, NavigationResult>();
private HashMap<String, NavigationResult> outcomes = new HashMap<String, NavigationResult>();
private boolean kidsPopulated = false;
/**
* Default constructor
*/
public NavigationConfigElement()
{
super("navigation");
}
/**
* Constructor
*
* @param name Name of the element this config element represents
*/
public NavigationConfigElement(String name)
{
super(name);
}
/**
* @see org.alfresco.config.ConfigElement#getChildren()
*/
public List<ConfigElement> getChildren()
{
// lazily build the list of generic config elements representing
// the navigation overrides as the caller may not even call this method
List<ConfigElement> kids = null;
if (this.viewIds.size() > 0 || this.outcomes.size() > 0)
{
if (this.kidsPopulated == false)
{
// create generic config elements for the from-view-id items
for (String fromViewId : this.viewIds.keySet())
{
GenericConfigElement ce = new GenericConfigElement(NavigationElementReader.ELEMENT_OVERRIDE);
ce.addAttribute(NavigationElementReader.ATTR_FROM_VIEWID, fromViewId);
NavigationResult navRes = this.viewIds.get(fromViewId);
String result = navRes.getResult();
if (navRes.isOutcome())
{
ce.addAttribute(NavigationElementReader.ATTR_TO_OUTCOME, result);
}
else
{
ce.addAttribute(NavigationElementReader.ATTR_TO_VIEWID, result);
}
// add the element
this.children.add(ce);
}
// create generic config elements for the from-outcome items
for (String fromOutcome : this.outcomes.keySet())
{
GenericConfigElement ce = new GenericConfigElement(NavigationElementReader.ELEMENT_OVERRIDE);
ce.addAttribute(NavigationElementReader.ATTR_FROM_OUTCOME, fromOutcome);
NavigationResult navRes = this.outcomes.get(fromOutcome);
String result = navRes.getResult();
if (navRes.isOutcome())
{
ce.addAttribute(NavigationElementReader.ATTR_TO_OUTCOME, result);
}
else
{
ce.addAttribute(NavigationElementReader.ATTR_TO_VIEWID, result);
}
// add the element
this.children.add(ce);
}
this.kidsPopulated = true;
}
kids = super.getChildren();
}
return kids;
}
/**
* @see org.alfresco.config.ConfigElement#combine(org.alfresco.config.ConfigElement)
*/
public ConfigElement combine(ConfigElement configElement)
{
NavigationConfigElement combined = new NavigationConfigElement();
// add all the existing from view id overrides
for (String fromViewId : this.viewIds.keySet())
{
combined.addOverride(fromViewId, null, this.viewIds.get(fromViewId));
}
// add all the existing from outcome overrides
for (String fromOutcome : this.outcomes.keySet())
{
combined.addOverride(null, fromOutcome, this.outcomes.get(fromOutcome));
}
// add all the from view id overrides from the given element
NavigationConfigElement navCfg = (NavigationConfigElement)configElement;
HashMap<String, NavigationResult> viewIds = navCfg.getViewIds();
for (String fromViewId : viewIds.keySet())
{
combined.addOverride(fromViewId, null, viewIds.get(fromViewId));
}
// add all the from outcome overrides from the given element
HashMap<String, NavigationResult> outcomes = navCfg.getOutcomes();
for (String fromOutcome : outcomes.keySet())
{
combined.addOverride(null, fromOutcome, outcomes.get(fromOutcome));
}
return combined;
}
/**
* Returns the list of view ids that have overrides defined
*
* @return Map of view ids and navigation results
*/
public HashMap<String, NavigationResult> getViewIds()
{
return this.viewIds;
}
/**
* Returns the list of outcomes that have overrides defined
*
* @return Map of outcomes and navigation results
*/
public HashMap<String, NavigationResult> getOutcomes()
{
return this.outcomes;
}
/**
* Adds an override configuration item
*
* @param fromViewId The from-view-id value from the config
* @param fromOutcome The from-outcome value from the config
* @param toViewId The to-view-id value from the config
* @param toOutcome The to-outcome value from the config
*/
public void addOverride(String fromViewId, String fromOutcome,
String toViewId, String toOutcome)
{
// NOTE: the constructor will check the validity of the to* parameters
NavigationResult result = new NavigationResult(toViewId, toOutcome);
addOverride(fromViewId, fromOutcome, result);
}
/**
* Adds an override configuration item
*
* @param fromViewId The from-view-id value from the config
* @param fromOutcome The from-outcome value from the config
* @param result The navigation result object to add
*/
public void addOverride(String fromViewId, String fromOutcome,
NavigationResult result)
{
if (fromViewId != null && fromOutcome != null)
{
throw new IllegalStateException("You can not have both a from-view-id and from-outcome");
}
if (fromViewId != null)
{
this.viewIds.put(fromViewId, result);
}
else if (fromOutcome != null)
{
this.outcomes.put(fromOutcome, result);
}
}
/**
* Returns the best match navigation override configured for the given
* current view id and/or outcome.
*
* If an outcome is passed it takes precedence, the view id will not be
* used.
*
* @param fromViewId The current view id
* @param fromOutcome The current outcome
* @return The navigation result
*/
public NavigationResult getOverride(String fromViewId, String fromOutcome)
{
NavigationResult result = null;
// look for a match for the outcome if one was provided
if (fromOutcome != null)
{
result = this.outcomes.get(fromOutcome);
}
else if (fromViewId != null)
{
result = this.viewIds.get(fromViewId);
}
return result;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.Iterator;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigException;
import org.alfresco.config.xml.elementreader.ConfigElementReader;
import org.dom4j.Element;
/**
* Custom element reader to parse config for navigation overrides
*
* @author gavinc
*/
public class NavigationElementReader implements ConfigElementReader
{
public static final String ELEMENT_NAVIGATION = "navigation";
public static final String ELEMENT_OVERRIDE = "override";
public static final String ATTR_FROM_VIEWID = "from-view-id";
public static final String ATTR_FROM_OUTCOME = "from-outcome";
public static final String ATTR_TO_VIEWID = "to-view-id";
public static final String ATTR_TO_OUTCOME = "to-outcome";
/**
* @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
*/
public ConfigElement parse(Element element)
{
NavigationConfigElement configElement = null;
if (element != null)
{
String name = element.getName();
if (ELEMENT_NAVIGATION.equals(name) == false)
{
throw new ConfigException("NavigationElementReader can only parse " +
ELEMENT_NAVIGATION + "elements, " + "the element passed was '" +
name + "'");
}
configElement = new NavigationConfigElement();
// go through the items to show
Iterator<Element> items = element.elementIterator();
while (items.hasNext())
{
Element item = items.next();
// only process the override elements
if (ELEMENT_OVERRIDE.equals(item.getName()))
{
String fromViewId = item.attributeValue(ATTR_FROM_VIEWID);
String fromOutcome = item.attributeValue(ATTR_FROM_OUTCOME);
String toViewId = item.attributeValue(ATTR_TO_VIEWID);
String toOutcome = item.attributeValue(ATTR_TO_OUTCOME);
configElement.addOverride(fromViewId, fromOutcome, toViewId, toOutcome);
}
}
}
return configElement;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under 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.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
/**
* Represents the result of a navigation config result.
*
* This object holds the string result which can either represent an outcome
* or a view id.
*
* @author gavinc
*/
public class NavigationResult
{
private String result;
private boolean isOutcome = true;
/**
* Default constructor
*
* @param viewId The to-view-id value
* @param outcome The to-outcome value
*/
public NavigationResult(String viewId, String outcome)
{
if (viewId != null && outcome != null)
{
throw new IllegalStateException("You can not have both a to-view-id and to-outcome");
}
if (outcome != null)
{
this.result = outcome;
}
else if (viewId != null)
{
this.result = viewId;
this.isOutcome = false;
}
}
/**
* Returns the result
*
* @return The result
*/
public String getResult()
{
return this.result;
}
/**
* Determines whether the result is an outcome
*
* @return true if the result represents an outcome,
* false if it represents a view id
*/
public boolean isOutcome()
{
return this.isOutcome;
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
StringBuilder buffer = new StringBuilder(super.toString());
buffer.append(" (result=").append(this.result);
buffer.append(" isOutcome=").append(this.isOutcome).append(")");
return buffer.toString();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import org.alfresco.config.evaluator.Evaluator;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
/**
* Evaluator that determines whether a given object has a particular node type
*
* @author gavinc
*/
public class NodeTypeEvaluator implements Evaluator
{
/**
* Determines whether the given node type matches the path of the given object
*
* @see org.alfresco.config.evaluator.Evaluator#applies(java.lang.Object, java.lang.String)
*/
public boolean applies(Object obj, String condition)
{
boolean result = false;
if (obj instanceof Node)
{
QName type = ((Node)obj).getType();
if (type != null)
{
result = type.equals(Repository.resolveToQName(condition));
}
}
return result;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import org.alfresco.config.evaluator.Evaluator;
import org.alfresco.web.bean.repository.Node;
/**
* Evaluator that determines whether a given object has a particular path
*
* @author gavinc
*/
public class PathEvaluator implements Evaluator
{
/**
* Determines whether the given path matches the path of the given object
*
* @see org.alfresco.config.evaluator.Evaluator#applies(java.lang.Object, java.lang.String)
*/
public boolean applies(Object obj, String condition)
{
boolean result = false;
// TODO: Also deal with NodeRef object's being passed in
if (obj instanceof Node)
{
String path = (String)((Node)obj).getPath();
if (path != null)
{
result = path.equalsIgnoreCase(condition);
}
}
return result;
}
}

View File

@@ -0,0 +1,324 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.element.ConfigElementAdapter;
import org.alfresco.config.element.GenericConfigElement;
/**
* Custom config element that represents the config data for a property sheet
*
* @author gavinc
*/
public class PropertySheetConfigElement extends ConfigElementAdapter
{
// TODO: Currently this object just deals with properties and associations to show,
// in the future it will also deal with properties and associations to hide.
private List<ItemConfig> items = new ArrayList<ItemConfig>();
private Map<String, ItemConfig> itemsMap = new HashMap<String, ItemConfig>();
private List<String> itemNames = new ArrayList<String>();
private boolean kidsPopulated = false;
/**
* Default constructor
*/
public PropertySheetConfigElement()
{
super("property-sheet");
}
/**
* Constructor
*
* @param name Name of the element this config element represents
*/
public PropertySheetConfigElement(String name)
{
super(name);
}
/**
* @see org.alfresco.config.ConfigElement#getChildren()
*/
public List<ConfigElement> getChildren()
{
// lazily build the list of generic config elements representing
// the properties as the caller may not even call this method
List<ConfigElement> kids = null;
if (this.items.size() > 0)
{
if (this.kidsPopulated == false)
{
Iterator<ItemConfig> items = this.items.iterator();
while (items.hasNext())
{
ItemConfig pc = items.next();
GenericConfigElement ce = null;
if (pc instanceof PropertyConfig)
{
ce = new GenericConfigElement(PropertySheetElementReader.ELEMENT_SHOW_PROPERTY);
}
else if (pc instanceof AssociationConfig)
{
ce = new GenericConfigElement(PropertySheetElementReader.ELEMENT_SHOW_ASSOC);
}
else
{
ce = new GenericConfigElement(PropertySheetElementReader.ELEMENT_SHOW_CHILD_ASSOC);
}
ce.addAttribute(PropertySheetElementReader.ATTR_NAME, pc.getName());
ce.addAttribute(PropertySheetElementReader.ATTR_DISPLAY_LABEL, pc.getDisplayLabel());
ce.addAttribute(PropertySheetElementReader.ATTR_DISPLAY_LABEL_ID, pc.getDisplayLabelId());
ce.addAttribute(PropertySheetElementReader.ATTR_READ_ONLY, Boolean.toString(pc.isReadOnly()));
ce.addAttribute(PropertySheetElementReader.ATTR_CONVERTER, pc.getConverter());
this.children.add(ce);
}
this.kidsPopulated = true;
}
kids = super.getChildren();
}
return kids;
}
/**
* @see org.alfresco.config.ConfigElement#combine(org.alfresco.config.ConfigElement)
*/
public ConfigElement combine(ConfigElement configElement)
{
PropertySheetConfigElement combined = new PropertySheetConfigElement();
// add all the existing properties
Iterator<ItemConfig> items = this.getItemsToShow().iterator();
while (items.hasNext())
{
combined.addItem(items.next());
}
// add all the properties from the given element
items = ((PropertySheetConfigElement)configElement).getItemsToShow().iterator();
while (items.hasNext())
{
combined.addItem(items.next());
}
return combined;
}
/**
* Adds an item to show
*
* @param itemConfig A pre-configured property or association config object
*/
public void addItem(ItemConfig itemConfig)
{
if (this.itemsMap.containsKey(itemConfig.getName()) == false)
{
this.items.add(itemConfig);
this.itemsMap.put(itemConfig.getName(), itemConfig);
this.itemNames.add(itemConfig.getName());
}
}
/**
* Adds a property to show
*
* @param name The name of the property
* @param displayLabel Display label to use for the property
* @param displayLabelId Display label message id to use for the property
* @param readOnly Sets whether the property should be rendered as read only
* @param converter The name of a converter to apply to the property control
*/
public void addProperty(String name, String displayLabel, String displayLabelId, String readOnly, String converter)
{
addItem(new PropertyConfig(name, displayLabel, displayLabelId, Boolean.parseBoolean(readOnly), converter));
}
/**
* Adds an association to show
*
* @param name The name of the association
* @param displayLabel Display label to use for the property
* @param displayLabelId Display label message id to use for the property
* @param readOnly Sets whether the association should be rendered as read only
* @param converter The name of a converter to apply to the association control
*/
public void addAssociation(String name, String displayLabel, String displayLabelId, String readOnly, String converter)
{
addItem(new AssociationConfig(name, displayLabel, displayLabelId, Boolean.parseBoolean(readOnly), converter));
}
/**
* Adds a child association to show
*
* @param name The name of the child association
* @param displayLabel Display label to use for the property
* @param displayLabelId Display label message id to use for the property
* @param readOnly Sets whether the association should be rendered as read only
* @param converter The name of a converter to apply to the association control
*/
public void addChildAssociation(String name, String displayLabel, String displayLabelId, String readOnly, String converter)
{
addItem(new ChildAssociationConfig(name, displayLabel, displayLabelId, Boolean.parseBoolean(readOnly), converter));
}
/**
* @return Returns a list of item names to display
*/
public List<String> getItemNamesToShow()
{
return this.itemNames;
}
/**
* @return Returns the list of item config objects that represent those to display
*/
public List<ItemConfig> getItemsToShow()
{
return this.items;
}
/**
* @return Returns a map of the item names to show
*/
public Map<String, ItemConfig> getItemsMapToShow()
{
return this.itemsMap;
}
/**
* Inner class to represent a configured property sheet item
*/
public abstract class ItemConfig
{
private String name;
private String displayLabel;
private String displayLabelId;
private String converter;
private boolean readOnly;
public ItemConfig(String name, String displayLabel, String displayLabelId,
boolean readOnly, String converter)
{
this.name = name;
this.displayLabel = displayLabel;
this.displayLabelId = displayLabelId;
this.readOnly = readOnly;
this.converter = converter;
}
/**
* @return The display label
*/
public String getDisplayLabel()
{
return this.displayLabel;
}
/**
* @return The display label message id
*/
public String getDisplayLabelId()
{
return this.displayLabelId;
}
/**
* @return The property name
*/
public String getName()
{
return this.name;
}
/**
* @return Determines whether the property is configured as read only
*/
public boolean isReadOnly()
{
return this.readOnly;
}
public String getConverter()
{
return this.converter;
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
StringBuilder buffer = new StringBuilder(super.toString());
buffer.append(" (name=").append(this.name);
buffer.append(" displaylabel=").append(this.displayLabel);
buffer.append(" displaylabelId=").append(this.displayLabelId);
buffer.append(" converter=").append(this.converter);
buffer.append(" readonly=").append(this.readOnly).append(")");
return buffer.toString();
}
}
/**
* Inner class to represent a configured property
*/
public class PropertyConfig extends ItemConfig
{
public PropertyConfig(String name, String displayLabel, String displayLabelId,
boolean readOnly, String converter)
{
super(name, displayLabel, displayLabelId, readOnly, converter);
}
}
/**
* Inner class to represent a configured association
*/
public class AssociationConfig extends ItemConfig
{
public AssociationConfig(String name, String displayLabel, String displayLabelId,
boolean readOnly, String converter)
{
super(name, displayLabel, displayLabelId, readOnly, converter);
}
}
/**
* Inner class to represent a configured child association
*/
public class ChildAssociationConfig extends ItemConfig
{
public ChildAssociationConfig(String name, String displayLabel, String displayLabelId,
boolean readOnly, String converter)
{
super(name, displayLabel, displayLabelId, readOnly, converter);
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.Iterator;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigException;
import org.alfresco.config.xml.elementreader.ConfigElementReader;
import org.dom4j.Element;
/**
* Custom element reader to parse config for property sheets
*
* @author gavinc
*/
public class PropertySheetElementReader implements ConfigElementReader
{
public static final String ELEMENT_PROPERTY_SHEET = "property-sheet";
public static final String ELEMENT_SHOW_PROPERTY = "show-property";
public static final String ELEMENT_SHOW_ASSOC = "show-association";
public static final String ELEMENT_SHOW_CHILD_ASSOC = "show-child-association";
public static final String ATTR_NAME = "name";
public static final String ATTR_DISPLAY_LABEL = "displayLabel";
public static final String ATTR_DISPLAY_LABEL_ID = "displayLabelId";
public static final String ATTR_READ_ONLY = "readOnly";
public static final String ATTR_CONVERTER = "converter";
/**
* @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
*/
public ConfigElement parse(Element element)
{
PropertySheetConfigElement configElement = null;
if (element != null)
{
String name = element.getName();
if (name.equals(ELEMENT_PROPERTY_SHEET) == false)
{
throw new ConfigException("PropertySheetElementReader can only parse " +
ELEMENT_PROPERTY_SHEET + "elements, " + "the element passed was '" +
name + "'");
}
configElement = new PropertySheetConfigElement();
// go through the items to show
Iterator<Element> items = element.elementIterator();
while (items.hasNext())
{
Element item = items.next();
String propName = item.attributeValue(ATTR_NAME);
String label = item.attributeValue(ATTR_DISPLAY_LABEL);
String labelId = item.attributeValue(ATTR_DISPLAY_LABEL_ID);
String readOnly = item.attributeValue(ATTR_READ_ONLY);
String converter = item.attributeValue(ATTR_CONVERTER);
if (ELEMENT_SHOW_PROPERTY.equals(item.getName()))
{
// add the property to show to the custom config element
configElement.addProperty(propName, label, labelId, readOnly, converter);
}
else if (ELEMENT_SHOW_ASSOC.equals(item.getName()))
{
configElement.addAssociation(propName, label, labelId, readOnly, converter);
}
else if (ELEMENT_SHOW_CHILD_ASSOC.equals(item.getName()))
{
configElement.addChildAssociation(propName, label, labelId, readOnly, converter);
}
}
}
return configElement;
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.element.ConfigElementAdapter;
/**
* Custom config element that represents the config data for the server
*
* @author gavinc
*/
public class ServerConfigElement extends ConfigElementAdapter
{
public static final String CONFIG_ELEMENT_ID = "server";
private String errorPage;
private String loginPage;
/**
* Default constructor
*/
public ServerConfigElement()
{
super(CONFIG_ELEMENT_ID);
}
/**
* Constructor
*
* @param name Name of the element this config element represents
*/
public ServerConfigElement(String name)
{
super(name);
}
public ConfigElement combine(ConfigElement configElement)
{
// NOTE: combining these would simply override the values so we just need
// to return a new instance of the given config element
ServerConfigElement combined = new ServerConfigElement();
combined.setErrorPage(((ServerConfigElement)configElement).getErrorPage());
combined.setLoginPage(((ServerConfigElement)configElement).getLoginPage());
return combined;
}
/**
* @return The error page the application should use
*/
public String getErrorPage()
{
return this.errorPage;
}
/**
* @param errorPage Sets the error page
*/
public void setErrorPage(String errorPage)
{
this.errorPage = errorPage;
}
/**
* @return Returns the login Page.
*/
public String getLoginPage()
{
return this.loginPage;
}
/**
* @param loginPage The login Page to set.
*/
public void setLoginPage(String loginPage)
{
this.loginPage = loginPage;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.ConfigException;
import org.alfresco.config.xml.elementreader.ConfigElementReader;
import org.dom4j.Element;
/**
* Custom element reader to parse config for server details
*
* @author gavinc
*/
public class ServerElementReader implements ConfigElementReader
{
public static final String ELEMENT_ERROR_PAGE = "error-page";
public static final String ELEMENT_LOGIN_PAGE = "login-page";
/**
* @see org.alfresco.config.xml.elementreader.ConfigElementReader#parse(org.dom4j.Element)
*/
public ConfigElement parse(Element element)
{
ServerConfigElement configElement = null;
if (element != null)
{
String name = element.getName();
if (name.equals(ServerConfigElement.CONFIG_ELEMENT_ID) == false)
{
throw new ConfigException("ServerElementReader can only parse " +
ServerConfigElement.CONFIG_ELEMENT_ID + "elements, " +
"the element passed was '" + name + "'");
}
configElement = new ServerConfigElement();
// get the error page
Element errorPage = element.element(ELEMENT_ERROR_PAGE);
if (errorPage != null)
{
configElement.setErrorPage(errorPage.getTextTrim());
}
// get the login page
Element loginPage = element.element(ELEMENT_LOGIN_PAGE);
if (loginPage != null)
{
configElement.setLoginPage(loginPage.getTextTrim());
}
}
return configElement;
}
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.config;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigElement;
import org.alfresco.config.source.FileConfigSource;
import org.alfresco.config.xml.XMLConfigService;
import org.alfresco.util.BaseTest;
import org.alfresco.web.config.PropertySheetConfigElement.ItemConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* JUnit tests to exercise the capabilities added to the web client config
* service
*
* @author gavinc
*/
public class WebClientConfigTest extends BaseTest
{
private static Log logger = LogFactory.getLog(WebClientConfigTest.class);
/**
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception
{
super.setUp();
logger.info("******************************************************");
}
/**
* Tests the property sheet configuration classes
*/
public void testPropertySheetConfig()
{
// setup the config service
String configFile = getResourcesDir() + "test-config.xml";
XMLConfigService svc = new XMLConfigService(new FileConfigSource(configFile));
svc.init();
// get hold of the property sheet config from the global section
Config global = svc.getGlobalConfig();
ConfigElement globalPropSheet = global.getConfigElement("property-sheet");
assertNotNull("global property sheet element should not be null", globalPropSheet);
assertTrue("config element should be an instance of PropertySheetConfigElement",
(globalPropSheet instanceof PropertySheetConfigElement));
// get the property names from the global section and make sure it is the
// name property
List<String> propNames = ((PropertySheetConfigElement) globalPropSheet).getItemNamesToShow();
logger.info("propNames = " + propNames);
assertTrue("There should only be one property in the list", propNames.size() == 1);
assertTrue("The property name should be 'name'", propNames.get(0).equals("name"));
// get the config section representing a space aspect and make sure we get
// 5 properties
Config spaceAspectConfig = svc.getConfig("space-aspect");
assertNotNull("Space aspect config should not be null", spaceAspectConfig);
PropertySheetConfigElement spacePropConfig = (PropertySheetConfigElement) spaceAspectConfig
.getConfigElement("property-sheet");
assertNotNull("Space aspect property config should not be null", spacePropConfig);
propNames = spacePropConfig.getItemNamesToShow();
logger.info("propNames = " + propNames);
assertTrue("There should be 5 properties in the list", propNames.size() == 5);
// make sure the property sheet config has come back with the correct data
Map<String, ItemConfig> props = spacePropConfig.getItemsMapToShow();
ItemConfig descProp = props.get("description");
assertNotNull("description property config should not be null", descProp);
assertEquals("display label for description should be 'Description'", descProp.getDisplayLabel(),
"Description");
assertFalse("read only for description should be 'false'", descProp.isReadOnly());
ItemConfig createdDataProp = props.get("createddate");
assertNotNull("createddate property config should not be null", createdDataProp);
assertEquals("display label for createddate should be null", null, createdDataProp.getDisplayLabel());
assertTrue("read only for createddate should be 'true'", createdDataProp.isReadOnly());
ItemConfig iconProp = props.get("icon");
assertNotNull("icon property config should not be null", iconProp);
assertEquals("display label for icon should be null", null, iconProp.getDisplayLabel());
assertFalse("read only for icon should be 'false'", iconProp.isReadOnly());
}
/**
* Tests the config service by retrieving property sheet configuration using
* the generic interfaces
*/
public void testGenericConfigElement()
{
// setup the config service
String configFiles = getResourcesDir() + "test-config.xml";
XMLConfigService svc = new XMLConfigService(new FileConfigSource(configFiles));
svc.init();
// get the space aspect configuration
Config configProps = svc.getConfig("space-aspect");
ConfigElement propsToDisplay = configProps.getConfigElement("property-sheet");
assertNotNull("property sheet config should not be null", propsToDisplay);
// get all the property names using the ConfigElement interface methods
List<ConfigElement> kids = propsToDisplay.getChildren();
List<String> propNames = new ArrayList<String>();
for (ConfigElement propElement : propsToDisplay.getChildren())
{
String value = propElement.getValue();
assertNull("property value should be null", value);
String propName = propElement.getAttribute("name");
propNames.add(propName);
}
logger.info("propNames = " + propNames);
assertTrue("There should be 5 properties", propNames.size() == 5);
assertFalse("The id attribute should not be present", propsToDisplay.hasAttribute("id"));
}
/**
* Tests the config service by retrieving property sheet configuration using
* the custom config objects
*/
public void testGetProperties()
{
// setup the config service
String configFiles = getResourcesDir() + "test-config.xml";
XMLConfigService svc = new XMLConfigService(new FileConfigSource(configFiles));
svc.init();
// get the space aspect configuration
Config configProps = svc.getConfig("space-aspect");
PropertySheetConfigElement propsToDisplay = (PropertySheetConfigElement)configProps.
getConfigElement("property-sheet");
assertNotNull("property sheet config should not be null", propsToDisplay);
// get all the property names using the PropertySheetConfigElement implementation
List<String> propNames = propsToDisplay.getItemNamesToShow();
// make sure the generic interfaces are also returning the correct data
List<ConfigElement> kids = propsToDisplay.getChildren();
assertNotNull("kids should not be null", kids);
assertTrue("There should be more than one child", kids.size() > 1);
logger.info("propNames = " + propNames);
assertEquals("There should be 5 properties", propNames.size() == 5, true);
assertFalse("The id attribute should not be present", propsToDisplay.hasAttribute("id"));
}
/**
* Tests the custom server configuration objects
*/
public void testServerConfig()
{
// setup the config service
String configFiles = getResourcesDir() + "test-config.xml";
XMLConfigService svc = new XMLConfigService(new FileConfigSource(configFiles));
svc.init();
// get the global config and from that the server config
ServerConfigElement serverConfig = (ServerConfigElement)svc.getGlobalConfig().
getConfigElement(ServerConfigElement.CONFIG_ELEMENT_ID);
assertNotNull("server config should not be null", serverConfig);
String errorPage = serverConfig.getErrorPage();
assertTrue("error page should be '/jsp/error.jsp'", errorPage.equals("/jsp/error.jsp"));
}
/**
* Tests the custom client configuration objects
*/
public void testClientConfig()
{
// setup the config service
String configFiles = getResourcesDir() + "test-config.xml";
XMLConfigService svc = new XMLConfigService(new FileConfigSource(configFiles));
svc.init();
// get the global config and from that the client config
ClientConfigElement clientConfig = (ClientConfigElement)svc.getGlobalConfig().
getConfigElement(ClientConfigElement.CONFIG_ELEMENT_ID);
assertNotNull("client config should not be null", clientConfig);
List<String> views = clientConfig.getViews();
assertEquals("There should be 2 configured views", 2, views.size());
String renderer = views.get(1);
assertEquals("Renderer for the icons view should be 'org.alfresco.web.ui.common.renderer.data.RichListRenderer.IconViewRenderer'",
"org.alfresco.web.ui.common.renderer.data.RichListRenderer.IconViewRenderer", renderer);
String defaultView = clientConfig.getDefaultView("topic");
assertEquals("Default view for topic should be 'bubble'", "bubble", defaultView);
// get the defualt view for something that doesn't exist
defaultView = clientConfig.getDefaultView("not-there");
assertEquals("Default view for missing view should be 'details'", "details", defaultView);
// get the default page size for the forum details view
int pageSize = clientConfig.getDefaultPageSize("forum", "details");
assertEquals("Page size for forum details should be 20", 20, pageSize);
// get the defualt page size for a non existent view
pageSize = clientConfig.getDefaultPageSize("not", "there");
assertEquals("Page size for forum details should be 10", 10, pageSize);
// get the default page size for a non existent screen and valid view
pageSize = clientConfig.getDefaultPageSize("not-there", "icons");
assertEquals("Page size for icons view should be 9", 9, pageSize);
// test the sort column
String column = clientConfig.getDefaultSortColumn("browse");
assertEquals("Sort column for browse should be 'name'", "name", column);
column = clientConfig.getDefaultSortColumn("topic");
assertEquals("Sort column for topic should be 'created'", "created", column);
// test the sorting direction
boolean sortDescending = clientConfig.hasDescendingSort("browse");
assertFalse("browse screen should use an ascending sort", sortDescending);
sortDescending = clientConfig.hasDescendingSort("topic");
assertTrue("topic screen should use a descending sort", sortDescending);
}
/**
* Tests the navigation config i.e. the custom element reader and config element
*/
public void testNavigation()
{
// setup the config service
String configFiles = getResourcesDir() + "test-config.xml";
XMLConfigService svc = new XMLConfigService(new FileConfigSource(configFiles));
svc.init();
// *** Test the returning of a view id override
Config testCfg = svc.getConfig("viewid-navigation-result");
assertNotNull("viewid-navigation-result config should not be null", testCfg);
NavigationConfigElement navCfg = (NavigationConfigElement)testCfg.getConfigElement("navigation");
assertNotNull("navigation config should not be null", navCfg);
// get the result for the browse view id
NavigationResult navResult = navCfg.getOverride("/jsp/browse/browse.jsp", null);
assertEquals("result should be '/jsp/forums/forums.jsp'", "/jsp/forums/forums.jsp",
navResult.getResult());
assertFalse("isOutcome test should be false", navResult.isOutcome());
// get the result for the browse outcome
navResult = navCfg.getOverride(null, "browse");
assertEquals("result should be '/jsp/forums/topics.jsp'", "/jsp/forums/topics.jsp",
navResult.getResult());
assertFalse("isOutcome test should be false", navResult.isOutcome());
// get the result when passing both the browse view id and outcome, make
// sure we get the result for the outcome as it should take precedence
navResult = navCfg.getOverride("/jsp/browse/browse.jsp", "browse");
assertEquals("result should be '/jsp/forums/topics.jsp'", "/jsp/forums/topics.jsp",
navResult.getResult());
assertFalse("isOutcome test should be false", navResult.isOutcome());
// *** Test the returning of an outcome override
testCfg = svc.getConfig("outcome-navigation-result");
assertNotNull("outcome-navigation-result config should not be null", testCfg);
navCfg = (NavigationConfigElement)testCfg.getConfigElement("navigation");
assertNotNull("navigation config should not be null", navCfg);
// get the result for the browse view id
navResult = navCfg.getOverride("/jsp/browse/browse.jsp", null);
assertEquals("result should be 'showSomethingElse'", "showSomethingElse",
navResult.getResult());
assertTrue("isOutcome test should be true", navResult.isOutcome());
// get the result for the browse outcome
navResult = navCfg.getOverride(null, "browse");
assertEquals("result should be 'showSomethingElse'", "showSomethingElse",
navResult.getResult());
assertTrue("isOutcome test should be true", navResult.isOutcome());
// get the result when passing both the browse view id and outcome, make
// sure we get the result for the outcome as it should take precedence
navResult = navCfg.getOverride("/jsp/browse/browse.jsp", "browse");
assertEquals("result should be 'showSomethingElse'", "showSomethingElse",
navResult.getResult());
assertTrue("isOutcome test should be true", navResult.isOutcome());
// *** Test the duplicate result config
testCfg = svc.getConfig("duplicate-navigation-overrides");
assertNotNull("duplicate-navigation-overrides config should not be null", testCfg);
navCfg = (NavigationConfigElement)testCfg.getConfigElement("navigation");
assertNotNull("navigation config should not be null", navCfg);
// make sure the outcome result is 'newOutcome'
navResult = navCfg.getOverride(null, "browse");
assertEquals("result should be 'newOutcome'", "newOutcome",
navResult.getResult());
assertTrue("isOutcome test should be true", navResult.isOutcome());
// call getOverride passing a valid view id but an invalid outcome
// and make sure the result is null
navResult = navCfg.getOverride("/jsp/browse/browse.jsp", "nonExistentOutcome");
assertNull("result should be null", navResult);
}
public void testNavigationGenericConfig()
{
// setup the config service
String configFiles = getResourcesDir() + "test-config.xml";
XMLConfigService svc = new XMLConfigService(new FileConfigSource(configFiles));
svc.init();
// do a lookup using the generic config elements and make sure the correct
// info comes out
Config testCfg = svc.getConfig("duplicate-navigation-overrides");
assertNotNull("duplicate-navigation-overrides config should not be null", testCfg);
ConfigElement ce = testCfg.getConfigElement("navigation");
assertNotNull("navigation config should not be null", ce);
List<ConfigElement> children = ce.getChildren();
assertNotNull(children);
// make sure there are 2 children
assertEquals("There should be 2 children", 2, children.size());
// get the first child and make sure the attributes are correct,
// from-view-id should be '/jsp/browse/browse.jsp' and to-view-id
// should be '/jsp/forums/forums.jsp'
ConfigElement child = children.get(0);
String fromViewId = child.getAttribute("from-view-id");
String fromOutcome = child.getAttribute("from-outcome");
String toViewId = child.getAttribute("to-view-id");
String toOutcome = child.getAttribute("to-outcome");
logger.info("fromViewId = " + fromViewId);
logger.info("fromOutcome = " + fromOutcome);
logger.info("toViewId = " + toViewId);
logger.info("toOutcome = " + toOutcome);
assertNull(fromOutcome);
assertNull(toOutcome);
assertEquals("/jsp/browse/browse.jsp", fromViewId);
assertEquals("/jsp/forums/forums.jsp", toViewId);
// get the second child and make sure the attributes are correct,
// from-outcome should be 'browse' and to-outcome should be 'newOutcome'
child = children.get(1);
fromViewId = child.getAttribute("from-view-id");
fromOutcome = child.getAttribute("from-outcome");
toViewId = child.getAttribute("to-view-id");
toOutcome = child.getAttribute("to-outcome");
logger.info("fromViewId = " + fromViewId);
logger.info("fromOutcome = " + fromOutcome);
logger.info("toViewId = " + toViewId);
logger.info("toOutcome = " + toOutcome);
assertNull(fromViewId);
assertNull(toViewId);
assertEquals("browse", fromOutcome);
assertEquals("newOutcome", toOutcome);
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.data;
/**
* @author kevinr
*/
public interface IDataContainer
{
/**
* Return the currently sorted column if any
*
* @return current sorted column if any
*/
public String getCurrentSortColumn();
/**
* Returns the current sort direction. Only valid if a sort column is set.
* True is returned for descending sort, false for accending sort.
*
* @return true for descending sort, false for accending sort
*/
public boolean isCurrentSortDescending();
/**
* Returns the current page size used for this list, or -1 for no paging.
*/
public int getPageSize();
/**
* Return the current page the list is displaying
*
* @return Current page with zero based index
*/
public int getCurrentPage();
/**
* Set the current page to display.
*
* @param index Zero based page index to display
*/
public void setCurrentPage(int index);
/**
* Return the count of max available pages
*
* @return count of max available pages
*/
public int getPageCount();
/**
* Returns true if a row of data is available
*
* @return true if data is available, false otherwise
*/
public boolean isDataAvailable();
/**
* Returns the next row of data from the data model
*
* @return next row of data as a Bean object
*/
public Object nextRow();
/**
* Sort the dataset using the specified sort parameters
*
* @param column Column to sort
* @param descending True for descending sort, false for ascending
* @param mode Sort mode to use (see IDataContainer constants)
*/
public void sort(String column, boolean descending, String mode);
public final static String SORT_CASEINSENSITIVE = "case-insensitive";
public final static String SORT_CASESENSITIVE = "case-sensitive";
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.data;
import java.util.List;
/**
* @author kevinr
*/
public final class MergeSort extends Sort
{
/**
* Constructor
*
* @param data a the List of String[] data to sort
* @param column the column getter method to use on the row to sort
* @param bForward true for a forward sort, false for a reverse sort
* @param mode sort mode to use (see IDataContainer constants)
*/
public MergeSort(List data, String column, boolean bForward, String mode)
{
super(data, column, bForward, mode);
}
// ------------------------------------------------------------------------------
// Sort Implementation
/**
* Runs the Quick Sort routine on the current dataset
*/
public void sort()
{
if (this.data.size() != 0)
{
// TODO: finish this!
//mergesort(this.data, 0, this.data.size() - 1);
/*a = this.data;
int n = a.length;
b = new int[(n+1) >> 1];
mergesort(0, n-1);*/
}
}
// ------------------------------------------------------------------------------
// Private methods
/*private static Object[] a, b;
private static void mergesort(int lo, int hi)
{
if (lo<hi)
{
int m=(lo+hi) >> 1;
mergesort(lo, m);
mergesort(m+1, hi);
merge(lo, m, hi);
}
}
private static void merge(int lo, int m, int hi)
{
int i, j, k;
i=0;
j=lo;
// copy first half of array a to auxiliary array b
while (j <= m)
{
b[i++] = a[j++];
}
i=0;
k=lo;
// copy back next-greatest element at each time
while (k < j && j <= hi)
{
if (b[i] <= a[j])
{
a[k++]=b[i++];
}
else
{
a[k++]=a[j++];
}
}
// copy back remaining elements of first half (if any)
while (k < j)
{
a[k++] = b[i++];
}
}*/
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.data;
import java.util.List;
/**
* QuickSort
*
* Implementation of a locale sensitive Quick Sort algorithm. The sorting supports
* locale specific case sensitive, case in-sensitive and numeric data sorting. The
* numeric sorting handles integer, floating point and scientific formats, with
* short-circuit value parsing.
*
* @author Kevin Roast
*/
public final class QuickSort extends Sort
{
/**
* Constructor
*
* @param data a the List of String[] data to sort
* @param column the column getter method to use on the row to sort
* @param bForward true for a forward sort, false for a reverse sort
* @param mode sort mode to use (see IDataContainer constants)
*/
public QuickSort(List data, String column, boolean bForward, String mode)
{
super(data, column, bForward, mode);
}
// ------------------------------------------------------------------------------
// Sort Implementation
/**
* Runs the Quick Sort routine on the current dataset
*/
public void sort()
{
if (this.data.size() != 0)
{
qsort(this.data, 0, this.data.size() - 1);
}
}
// ------------------------------------------------------------------------------
// Private methods
/**
* recursive Quicksort function.
*
* @param v the array out of which to take a slice.
* @param lower the lower bound of this slice.
* @param upper the upper bound of this slice.
*/
private void qsort(final List v, final int lower, final int upper)
{
int sliceLength = upper - lower + 1 ;
if (sliceLength > 1)
{
if (sliceLength < 7)
{
// Insertion sort on smallest datasets
for (int i=lower; i<=upper; i++)
{
if (this.bForward == true)
{
for (int j=i; j > lower && getComparator().compare(this.keys.get(j - 1), this.keys.get(j)) > 0; j--)
{
// swap both the keys and the actual row data
swap(this.keys, j - 1, j);
swap(v, j - 1, j);
}
}
else
{
for (int j=i; j > lower && getComparator().compare(this.keys.get(j - 1), this.keys.get(j)) < 0; j--)
{
// swap both the keys and the actual row data
swap(this.keys, j - 1, j);
swap(v, j - 1, j);
}
}
}
}
else
{
int pivotIndex = partition(v, lower, upper);
qsort(v, lower, pivotIndex);
qsort(v, pivotIndex + 1, upper);
}
}
}
/**
* Partition an array in two using the pivot value that is at the
* centre of the array being partitioned.
*
* This partition implementation based on that in Winder, R
* (1993) "Developing C++ Software", Wiley, p.395. NB. This
* implementation (unlike most others) does not guarantee that
* the split point contains the pivot value. Unlike other
* implementations, it requires only < (or >) relation and not
* both < and <= (or > and >=). Also, it seems easier to program
* and to understand.
*
* @param v the List out of which to take a slice.
* @param lower the lower bound of this slice.
* @param upper the upper bound of this slice.
*/
private int partition(final List v, int lower, int upper)
{
List keys = this.keys;
Object pivotValue = keys.get((upper + lower + 1) >> 1) ;
int size = keys.size();
while (lower <= upper)
{
if (this.bForward == true)
{
while (getComparator().compare(keys.get(lower), pivotValue) < 0)
{
lower++;
}
while (getComparator().compare(pivotValue, keys.get(upper)) < 0)
{
upper--;
}
}
else
{
while (getComparator().compare(keys.get(lower), pivotValue) > 0)
{
lower++;
}
while (getComparator().compare(pivotValue, keys.get(upper)) > 0)
{
upper--;
}
}
if (lower <= upper)
{
if (lower < upper)
{
swap(keys, lower, upper);
swap(v, lower, upper);
}
lower++;
upper--;
}
}
return upper;
}
} // end class QuickSort

View File

@@ -0,0 +1,440 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.data;
import java.lang.reflect.Method;
import java.text.CollationKey;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.log4j.Logger;
/**
* Sort
*
* Base sorting helper supports locale specific case sensitive, case in-sensitive and
* numeric data sorting.
*
* @author Kevin Roast
*/
public abstract class Sort
{
// ------------------------------------------------------------------------------
// Construction
/**
* Constructor
*
* @param data a the List of String[] data to sort
* @param column the column getter method to use on the row to sort
* @param bForward true for a forward sort, false for a reverse sort
* @param mode sort mode to use (see IDataContainer constants)
*/
public Sort(List data, String column, boolean bForward, String mode)
{
this.data = data;
this.column = column;
this.bForward = bForward;
this.sortMode = mode;
if (this.data.size() != 0)
{
// setup the Collator for our Locale
Collator collator = Collator.getInstance(Locale.getDefault());
// set the strength according to the sort mode
if (mode.equals(IDataContainer.SORT_CASEINSENSITIVE))
{
collator.setStrength(Collator.SECONDARY);
}
else
{
collator.setStrength(Collator.IDENTICAL);
}
this.keys = buildCollationKeys(collator);
}
}
// ------------------------------------------------------------------------------
// Abstract Methods
/**
* Runs the Sort routine on the current dataset
*/
public abstract void sort();
// ------------------------------------------------------------------------------
// Helper methods
/**
* Build a list of collation keys for comparing locale sensitive strings or build
* the appropriate objects for comparison for other standard data types.
*
* @param collator the Collator object to use to build String keys
*/
protected List buildCollationKeys(Collator collator)
{
List data = this.data;
int iSize = data.size();
List keys = new ArrayList(iSize);
try
{
// create the Bean getter method invoker to retrieve the value for a colunm
String methodName = getGetterMethodName(this.column);
Class returnType = null;;
Method getter = null;
// there will always be at least one item to sort if we get to this method
Object bean = this.data.get(0);
try
{
getter = bean.getClass().getMethod(methodName, (Class [])null);
returnType = getter.getReturnType();
}
catch (NoSuchMethodException nsmerr)
{
// no bean getter method found - try Map implementation
if (bean instanceof Map)
{
Object obj = ((Map)bean).get(this.column);
if (obj != null)
{
returnType = obj.getClass();
}
else
{
if (s_logger.isInfoEnabled())
{
s_logger.info("Unable to get return type class for RichList column: " + column +
". Suggest set java type directly in sort component tag.");
}
returnType = Object.class;
}
}
else
{
throw new IllegalStateException("Unable to find bean getter or Map impl for column name: " + this.column);
}
}
// create appropriate comparator instance based on data type
// using the strategy pattern so sub-classes of Sort simply invoke the
// compare() method on the comparator interface - no type info required
boolean bknownType = true;
if (returnType.equals(String.class))
{
if (strongStringCompare == true)
{
this.comparator = new StringComparator();
}
else
{
this.comparator = new SimpleStringComparator();
}
}
else if (returnType.equals(Date.class))
{
this.comparator = new DateComparator();
}
else if (returnType.equals(boolean.class) || returnType.equals(Boolean.class))
{
this.comparator = new BooleanComparator();
}
else if (returnType.equals(int.class) || returnType.equals(Integer.class))
{
this.comparator = new IntegerComparator();
}
else if (returnType.equals(long.class) || returnType.equals(Long.class))
{
this.comparator = new LongComparator();
}
else if (returnType.equals(float.class) || returnType.equals(Float.class))
{
this.comparator = new FloatComparator();
}
else
{
s_logger.warn("Unsupported sort data type: " + returnType + " defaulting to .toString()");
this.comparator = new SimpleComparator();
bknownType = false;
}
// create a collation key for each required column item in the dataset
for (int iIndex=0; iIndex<iSize; iIndex++)
{
Object obj;
if (getter != null)
{
// if we have a bean getter method impl use that
obj = getter.invoke(data.get(iIndex), (Object [])null);
}
else
{
// else we must have a bean Map impl
obj = ((Map)data.get(iIndex)).get(column);
}
if (obj instanceof String)
{
String str = (String)obj;
if (strongStringCompare == true)
{
if (str.indexOf(' ') != -1)
{
// quote white space characters or they will be ignored by the Collator!
int iLength = str.length();
StringBuilder s = new StringBuilder(iLength + 4);
char c;
for (int i=0; i<iLength; i++)
{
c = str.charAt(i);
if (c != ' ')
{
s.append(c);
}
else
{
s.append('\'').append(c).append('\'');
}
}
str = s.toString();
}
keys.add(collator.getCollationKey(str));
}
else
{
keys.add(str);
}
}
else if (bknownType == true)
{
// the appropriate wrapper object will be create by the reflection
// system to wrap primative types e.g. int and boolean.
// therefore the correct type will be ready for use by the comparator
keys.add(obj);
}
else
{
if (obj != null)
{
keys.add(obj.toString());
}
else
{
keys.add(null);
}
}
}
}
catch (Exception err)
{
throw new RuntimeException(err);
}
return keys;
}
/**
* Given the array and two indices, swap the two items in the
* array.
*/
protected void swap(final List v, final int a, final int b)
{
Object temp = v.get(a);
v.set(a, v.get(b));
v.set(b, temp);
}
/**
* Return the comparator to be used during column value comparison
*
* @return Comparator for the appropriate column data type
*/
protected Comparator getComparator()
{
return this.comparator;
}
/**
* Return the name of the Bean getter method for the specified getter name
*
* @param name of the field to build getter method name for e.g. "value"
*
* @return the name of the Bean getter method for the field name e.g. "getValue"
*/
protected static String getGetterMethodName(String name)
{
return "get" + name.substring(0, 1).toUpperCase() +
name.substring(1, name.length());
}
// ------------------------------------------------------------------------------
// Inner classes for data type comparison
private class SimpleComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return (obj1.toString()).compareTo(obj2.toString());
}
}
private class SimpleStringComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return ((String)obj1).compareToIgnoreCase((String)obj2);
}
}
private class StringComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return ((CollationKey)obj1).compareTo((CollationKey)obj2);
}
}
private class IntegerComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return ((Integer)obj1).compareTo((Integer)obj2);
}
}
private class FloatComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return ((Float)obj1).compareTo((Float)obj2);
}
}
private class LongComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return ((Long)obj1).compareTo((Long)obj2);
}
}
private class BooleanComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return ((Boolean)obj1).equals((Boolean)obj2) ? -1 : 1;
}
}
private class DateComparator implements Comparator
{
/**
* @see org.alfresco.web.data.IDataComparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(final Object obj1, final Object obj2)
{
if (obj1 == null && obj2 == null) return 0;
if (obj1 == null) return -1;
if (obj2 == null) return 1;
return ((Date)obj1).compareTo((Date)obj2);
}
}
// ------------------------------------------------------------------------------
// Private Data
/** list of Object[] data to sort */
protected List data;
/** column name to sort against */
protected String column;
/** sort direction */
protected boolean bForward;
/** sort mode (see IDataContainer constants) */
protected String sortMode;
/** locale sensitive collator */
protected Collator collator;
/** collation keys for comparisons */
protected List keys = null;
/** the comparator instance to use for comparing values when sorting */
private Comparator comparator = null;
// TODO: make this configurable
/** config value whether to use strong collation Key string comparisons */
private boolean strongStringCompare = false;
private static Logger s_logger = Logger.getLogger(IDataContainer.class);
} // end class Sort

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.common;
/**
* @author Kevin Roast
*/
public final class ComponentConstants
{
public static final String JAVAX_FACES_INPUT = "javax.faces.Input";
public static final String JAVAX_FACES_TEXT = "javax.faces.Text";
public static final String JAVAX_FACES_OUTPUT = "javax.faces.Output";
public static final String JAVAX_FACES_GRID = "javax.faces.Grid";
public static final String JAVAX_FACES_PANEL = "javax.faces.Panel";
public static final String JAVAX_FACES_CHECKBOX = "javax.faces.Checkbox";
public static final String JAVAX_FACES_SELECT_BOOLEAN = "javax.faces.SelectBoolean";
/**
* Private constructor
*/
private ComponentConstants()
{
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.common;
import javax.faces.component.StateHolder;
import javax.faces.context.FacesContext;
import javax.faces.el.MethodBinding;
public class ConstantMethodBinding extends MethodBinding implements StateHolder
{
private String outcome = null;
private boolean transientFlag = false;
public ConstantMethodBinding()
{
}
public ConstantMethodBinding(String yourOutcome)
{
outcome = yourOutcome;
}
public Object invoke(FacesContext context, Object params[])
{
return outcome;
}
public Class getType(FacesContext context)
{
return String.class;
}
public Object saveState(FacesContext context)
{
return outcome;
}
public void restoreState(FacesContext context, Object state)
{
outcome = (String) state;
}
public boolean isTransient()
{
return (this.transientFlag);
}
public void setTransient(boolean transientFlag)
{
this.transientFlag = transientFlag;
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.common;
import java.io.IOException;
import java.io.Writer;
/**
* Helper to generate the rounded panel HTML templates
*
* @author kevinr
*/
public final class PanelGenerator
{
public static void generatePanel(Writer out, String contextPath, String panel, String inner)
throws IOException
{
generatePanel(out, contextPath, panel, inner, BGCOLOR_WHITE);
}
public static void generatePanel(Writer out, String contextPath, String panel, String inner, String bgColor)
throws IOException
{
generatePanel(out, contextPath, panel, inner, bgColor, false);
}
public static void generatePanel(Writer out, String contextPath, String panel, String inner, String bgColor, boolean dialog)
throws IOException
{
generatePanelStart(out, contextPath, panel, bgColor, dialog);
out.write(inner);
generatePanelEnd(out, contextPath, panel);
}
public static void generatePanelStart(Writer out, String contextPath, String panel, String bgColor)
throws IOException
{
generatePanelStart(out, contextPath, panel, bgColor, false);
}
public static void generatePanelStart(Writer out, String contextPath, String panel, String bgColor, boolean dialog)
throws IOException
{
out.write("<table cellspacing=0 cellpadding=0 border=0 width=100%><tr><td width=7><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_01.gif' width=7 height=7 alt=''></td>");
out.write("<td background='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_02.gif'><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_02.gif' width=7 height=7 alt=''></td>");
out.write("<td width=7><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_03");
if (dialog)
{
out.write("_squared");
}
out.write(".gif' width=7 height=7 alt=''></td></tr>");
out.write("<tr><td background='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_04.gif'><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_04.gif' width=7 height=7 alt=''></td><td bgcolor='");
out.write(bgColor);
out.write("'>");
}
public static void generatePanelEnd(Writer out, String contextPath, String panel)
throws IOException
{
out.write("</td><td background='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_06.gif'><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_06.gif' width=7 height=7 alt=''></td></tr>");
out.write("<tr><td width=7><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_07.gif' width=7 height=7 alt=''></td>");
out.write("<td background='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_08.gif'><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_08.gif' width=7 height=7 alt=''></td>");
out.write("<td width=7><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(panel);
out.write("_09.gif' width=7 height=7 alt=''></td></tr></table>");
}
public static void generateTitledPanelMiddle(Writer out, String contextPath, String titlePanel,
String contentPanel, String contentBgColor) throws IOException
{
// generate the expanded part, just under the title
out.write("</td><td background='");
out.write(contextPath);
out.write("/images/parts/");
out.write(titlePanel);
out.write("_06.gif'><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(titlePanel);
out.write("_06.gif' width=7 height=7 alt=''></td></tr>");
out.write("<tr><td width=7><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(titlePanel);
out.write("_");
out.write(contentPanel);
out.write("_07.gif' width=7 height=7 alt=''></td>");
out.write("<td background='");
out.write(contextPath);
out.write("/images/parts/");
out.write(titlePanel);
out.write("_08.gif'><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(titlePanel);
out.write("_08.gif' width=7 height=7 alt=''></td>");
out.write("<td width=7><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(titlePanel);
out.write("_");
out.write(contentPanel);
out.write("_09.gif' width=7 height=7 alt=''></td></tr>");
out.write("<tr><td background='");
out.write(contextPath);
out.write("/images/parts/");
out.write(contentPanel);
out.write("_04.gif'><img src='");
out.write(contextPath);
out.write("/images/parts/");
out.write(contentPanel);
out.write("_04.gif' width=7 height=7 alt=''></td><td bgcolor='");
out.write(contentBgColor);
out.write("' style='padding-top:6px;'>");
}
public final static String BGCOLOR_WHITE = "#FFFFFF";
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.common;
import javax.faces.model.SelectItem;
/**
* Wrapper class to facilitate case-insensitive sorting functionality against our SelectItem objects
*
* @author Kevin Roast
*/
public final class SortableSelectItem extends SelectItem implements Comparable
{
public SortableSelectItem(String value, String label, String sort)
{
super(value, label);
this.sort = sort;
}
public int compareTo(Object obj2)
{
if (this.sort == null && obj2 == null) return 0;
if (this.sort == null) return -1;
if (obj2 == null) return 1;
return this.sort.compareToIgnoreCase( ((SortableSelectItem)obj2).sort );
}
private String sort;
}

View File

@@ -0,0 +1,996 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.common;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.application.FacesMessage;
import javax.faces.component.NamingContainer;
import javax.faces.component.UIComponent;
import javax.faces.component.UIForm;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.el.EvaluationException;
import javax.faces.el.MethodBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.CIFSServer;
import org.alfresco.filesys.server.filesys.DiskSharedDevice;
import org.alfresco.filesys.smb.server.repo.ContentContext;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.webdav.WebDAVServlet;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.DownloadContentServlet;
import org.alfresco.web.app.servlet.ExternalAccessServlet;
import org.alfresco.web.bean.NavigationBean;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.data.IDataContainer;
import org.alfresco.web.ui.common.component.UIStatusMessage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.renderkit.html.HtmlFormRendererBase;
import org.springframework.web.jsf.FacesContextUtils;
/**
* Class containing misc helper methods used by the JSF components.
*
* @author Kevin Roast
*/
public final class Utils
{
private static final String MSG_TIME_PATTERN = "time_pattern";
private static final String MSG_DATE_PATTERN = "date_pattern";
private static final String MSG_DATE_TIME_PATTERN = "date_time_pattern";
private static final String IMAGE_PREFIX16 = "/images/filetypes/";
private static final String IMAGE_PREFIX32 = "/images/filetypes32/";
private static final String IMAGE_POSTFIX = ".gif";
private static final String DEFAULT_FILE_IMAGE16 = IMAGE_PREFIX16 + "_default" + IMAGE_POSTFIX;
private static final String DEFAULT_FILE_IMAGE32 = IMAGE_PREFIX32 + "_default" + IMAGE_POSTFIX;
private static final Map<String, String> s_fileExtensionMap = new HashMap<String, String>(89, 1.0f);
private static Log logger = LogFactory.getLog(Utils.class);
/**
* Private constructor
*/
private Utils()
{
}
/**
* Encodes the given string, so that it can be used within an HTML page.
*
* @param string the String to convert
*/
public static String encode(String string)
{
if (string == null)
{
return "";
}
StringBuilder sb = null; //create on demand
String enc;
char c;
for (int i = 0; i < string.length(); i++)
{
enc = null;
c = string.charAt(i);
switch (c)
{
case '"': enc = "&quot;"; break; //"
case '&': enc = "&amp;"; break; //&
case '<': enc = "&lt;"; break; //<
case '>': enc = "&gt;"; break; //>
//german umlauts
case '\u00E4' : enc = "&auml;"; break;
case '\u00C4' : enc = "&Auml;"; break;
case '\u00F6' : enc = "&ouml;"; break;
case '\u00D6' : enc = "&Ouml;"; break;
case '\u00FC' : enc = "&uuml;"; break;
case '\u00DC' : enc = "&Uuml;"; break;
case '\u00DF' : enc = "&szlig;"; break;
//misc
//case 0x80: enc = "&euro;"; break; sometimes euro symbol is ascii 128, should we suport it?
case '\u20AC': enc = "&euro;"; break;
case '\u00AB': enc = "&laquo;"; break;
case '\u00BB': enc = "&raquo;"; break;
case '\u00A0': enc = "&nbsp;"; break;
default:
if (((int)c) >= 0x80)
{
//encode all non basic latin characters
enc = "&#" + ((int)c) + ";";
}
break;
}
if (enc != null)
{
if (sb == null)
{
String soFar = string.substring(0, i);
sb = new StringBuilder(i + 8);
sb.append(soFar);
}
sb.append(enc);
}
else
{
if (sb != null)
{
sb.append(c);
}
}
}
if (sb == null)
{
return string;
}
else
{
return sb.toString();
}
}
/**
* Crop a label within a SPAN element, using ellipses '...' at the end of label and
* and encode the result for HTML output. A SPAN will only be generated if the label
* is beyond the default setting of 32 characters in length.
*
* @param text to crop and encode
*
* @return encoded and cropped resulting label HTML
*/
public static String cropEncode(String text)
{
return cropEncode(text, 32);
}
/**
* Crop a label within a SPAN element, using ellipses '...' at the end of label and
* and encode the result for HTML output. A SPAN will only be generated if the label
* is beyond the specified number of characters in length.
*
* @param text to crop and encode
* @param length length of string to crop too
*
* @return encoded and cropped resulting label HTML
*/
public static String cropEncode(String text, int length)
{
if (text.length() > length)
{
String label = text.substring(0, length - 3) + "...";
StringBuilder buf = new StringBuilder(length + 32 + text.length());
buf.append("<span title=\"")
.append(Utils.encode(text))
.append("\">")
.append(Utils.encode(label))
.append("</span>");
return buf.toString();
}
else
{
return Utils.encode(text);
}
}
/**
* Replace one string instance with another within the specified string
*
* @param str
* @param repl
* @param with
*
* @return replaced string
*/
public static String replace(String str, String repl, String with)
{
int lastindex = 0;
int pos = str.indexOf(repl);
// If no replacement needed, return the original string
// and save StringBuffer allocation/char copying
if (pos < 0)
{
return str;
}
int len = repl.length();
int lendiff = with.length() - repl.length();
StringBuilder out = new StringBuilder((lendiff <= 0) ? str.length() : (str.length() + (lendiff << 3)));
for (; pos >= 0; pos = str.indexOf(repl, lastindex = pos + len))
{
out.append(str.substring(lastindex, pos)).append(with);
}
return out.append(str.substring(lastindex, str.length())).toString();
}
/**
* Remove all occurances of a String from a String
*
* @param str String to remove occurances from
* @param match The string to remove
*
* @return new String with occurances of the match removed
*/
public static String remove(String str, String match)
{
int lastindex = 0;
int pos = str.indexOf(match);
// If no replacement needed, return the original string
// and save StringBuffer allocation/char copying
if (pos < 0)
{
return str;
}
int len = match.length();
StringBuilder out = new StringBuilder(str.length());
for (; pos >= 0; pos = str.indexOf(match, lastindex = pos + len))
{
out.append(str.substring(lastindex, pos));
}
return out.append(str.substring(lastindex, str.length())).toString();
}
/**
* Helper to output an attribute to the output stream
*
* @param out ResponseWriter
* @param attr attribute value object (cannot be null)
* @param mapping mapping to output as e.g. style="..."
*
* @throws IOException
*/
public static void outputAttribute(ResponseWriter out, Object attr, String mapping)
throws IOException
{
if (attr != null)
{
out.write(' ');
out.write(mapping);
out.write("=\"");
out.write(attr.toString());
out.write('"');
}
}
/**
* Get the hidden field name for any action component.
*
* All components that wish to simply encode a form value with their client ID can reuse the same
* hidden field within the parent form. NOTE: components which use this method must only encode
* their client ID as the value and nothing else!
*
* Build a shared field name from the parent form name and the string "act".
*
* @return hidden field name shared by all action components within the Form.
*/
public static String getActionHiddenFieldName(FacesContext context, UIComponent component)
{
return Utils.getParentForm(context, component).getClientId(context) + NamingContainer.SEPARATOR_CHAR + "act";
}
/**
* Helper to recursively render a component and it's child components
*
* @param context FacesContext
* @param component UIComponent
*
* @throws IOException
*/
public static void encodeRecursive(FacesContext context, UIComponent component)
throws IOException
{
if (component.isRendered() == true)
{
component.encodeBegin(context);
// follow the spec for components that render their children
if (component.getRendersChildren() == true)
{
component.encodeChildren(context);
}
else
{
if (component.getChildCount() != 0)
{
for (Iterator i=component.getChildren().iterator(); i.hasNext(); /**/)
{
encodeRecursive(context, (UIComponent)i.next());
}
}
}
component.encodeEnd(context);
}
}
/**
* Generate the JavaScript to submit set the specified hidden Form field to the
* supplied value and submit the parent Form.
*
* NOTE: the supplied hidden field name is added to the Form Renderer map for output.
*
* @param context FacesContext
* @param component UIComponent to generate JavaScript for
* @param fieldId Hidden field id to set value for
* @param fieldValue Hidden field value to set hidden field too on submit
*
* @return JavaScript event code
*/
public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId, String fieldValue)
{
return generateFormSubmit(context, component, fieldId, fieldValue, null);
}
/**
* Generate the JavaScript to submit set the specified hidden Form field to the
* supplied value and submit the parent Form.
*
* NOTE: the supplied hidden field name is added to the Form Renderer map for output.
*
* @param context FacesContext
* @param component UIComponent to generate JavaScript for
* @param fieldId Hidden field id to set value for
* @param fieldValue Hidden field value to set hidden field too on submit
* @param params Optional map of param name/values to output
*
* @return JavaScript event code
*/
public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId, String fieldValue, Map<String, String> params)
{
UIForm form = Utils.getParentForm(context, component);
if (form == null)
{
throw new IllegalStateException("Must nest components inside UIForm to generate form submit!");
}
String formClientId = form.getClientId(context);
StringBuilder buf = new StringBuilder(200);
buf.append("document.forms[");
buf.append("'");
buf.append(formClientId);
buf.append("'");
buf.append("]['");
buf.append(fieldId);
buf.append("'].value='");
buf.append(fieldValue);
buf.append("';");
if (params != null)
{
for (String name : params.keySet())
{
buf.append("document.forms[");
buf.append("'");
buf.append(formClientId);
buf.append("'");
buf.append("]['");
buf.append(name);
buf.append("'].value='");
buf.append(params.get(name));
buf.append("';");
// weak, but this seems to be the way Sun RI do it...
//FormRenderer.addNeededHiddenField(context, name);
HtmlFormRendererBase.addHiddenCommandParameter(form, name);
}
}
buf.append("document.forms[");
buf.append("'");
buf.append(formClientId);
buf.append("'");
buf.append("].submit()");
buf.append(";return false;");
// weak, but this seems to be the way Sun RI do it...
//FormRenderer.addNeededHiddenField(context, fieldId);
HtmlFormRendererBase.addHiddenCommandParameter(form, fieldId);
return buf.toString();
}
/**
* Generate the JavaScript to submit the parent Form.
*
* @param context FacesContext
* @param component UIComponent to generate JavaScript for
*
* @return JavaScript event code
*/
public static String generateFormSubmit(FacesContext context, UIComponent component)
{
UIForm form = Utils.getParentForm(context, component);
if (form == null)
{
throw new IllegalStateException("Must nest components inside UIForm to generate form submit!");
}
String formClientId = form.getClientId(context);
StringBuilder buf = new StringBuilder(48);
buf.append("document.forms[");
buf.append("'");
buf.append(formClientId);
buf.append("'");
buf.append("].submit()");
buf.append(";return false;");
return buf.toString();
}
/**
* Enum representing the client URL type to generate
*/
public enum URLMode {HTTP_DOWNLOAD, HTTP_INLINE, WEBDAV, CIFS, SHOW_DETAILS, FTP}
/**
* Generates a URL for the given usage for the given node.
*
* The supported values for the usage parameter are of URLMode enum type
* @see URLMode
*
* @param context Faces context
* @param node The node to generate the URL for
* @param usage What the URL is going to be used for
* @return The URL for the requested usage without the context path
*/
public static String generateURL(FacesContext context, Node node, URLMode usage)
{
String url = null;
switch (usage)
{
case WEBDAV:
{
// calculate a WebDAV URL for the given node
FileFolderService fileFolderService = Repository.getServiceRegistry(
context).getFileFolderService();
try
{
List<FileInfo> paths = fileFolderService.getNamePath(null, node.getNodeRef());
User user = Application.getCurrentUser(context);
// build up the webdav url
StringBuilder path = new StringBuilder("/").append(WebDAVServlet.WEBDAV_PREFIX);
// build up the path skipping the first path as it is the root folder
boolean homeSpaceFound = false;
for (int x = 1; x < paths.size(); x++)
{
path.append("/").append(paths.get(x).getName());
}
url = path.toString();
}
catch (Exception e)
{
if (logger.isWarnEnabled())
logger.warn("Failed to calculate webdav url", e);
}
break;
}
case CIFS:
{
// calculate a CIFS path for the given node
// get hold of the node service, cifsServer and navigation bean
NodeService nodeService = Repository.getServiceRegistry(context).getNodeService();
NavigationBean navBean = (NavigationBean)context.getExternalContext().
getSessionMap().get("NavigationBean");
CIFSServer cifsServer = (CIFSServer)FacesContextUtils.getRequiredWebApplicationContext(
context).getBean("cifsServer");
if (nodeService != null && navBean != null && cifsServer != null)
{
DiskSharedDevice diskShare = cifsServer.getConfiguration().getPrimaryFilesystem();
if (diskShare != null)
{
ContentContext contentCtx = (ContentContext) diskShare.getContext();
NodeRef rootNode = contentCtx.getRootNode();
Path path = nodeService.getPath(node.getNodeRef());
url = Repository.getNamePath(nodeService, path, rootNode, "\\",
"file:///" + navBean.getCIFSServerPath(diskShare));
}
}
break;
}
case HTTP_DOWNLOAD:
{
url = DownloadContentServlet.generateDownloadURL(node.getNodeRef(), node.getName());
break;
}
case HTTP_INLINE:
{
url = DownloadContentServlet.generateBrowserURL(node.getNodeRef(), node.getName());
break;
}
case SHOW_DETAILS:
{
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();
// default to showing details of content
String outcome = "showDocDetails";
// if the node is a type of folder then make the outcome to show space details
if (dd.isSubClass(node.getType(), ContentModel.TYPE_FOLDER))
{
outcome = "showSpaceDetails";
}
// build the url
url = ExternalAccessServlet.generateExternalURL(outcome,
Repository.getStoreRef().getProtocol() + "/" +
Repository.getStoreRef().getIdentifier() + "/" + node.getId());
break;
}
case FTP:
{
// not implemented yet!
break;
}
}
return url;
}
/**
* Build a context path safe image tag for the supplied image path.
* Image path should be supplied with a leading slash '/'.
*
* @param context FacesContext
* @param image The local image path from the web folder with leading slash '/'
* @param width Width in pixels
* @param height Height in pixels
* @param alt Optional alt/title text
* @param onclick JavaScript onclick event handler code
*
* @return Populated <code>img</code> tag
*/
public static String buildImageTag(FacesContext context, String image, int width, int height, String alt, String onclick)
{
return buildImageTag(context, image, width, height, alt, onclick, null);
}
/**
* Build a context path safe image tag for the supplied image path.
* Image path should be supplied with a leading slash '/'.
*
* @param context FacesContext
* @param image The local image path from the web folder with leading slash '/'
* @param width Width in pixels
* @param height Height in pixels
* @param alt Optional alt/title text
* @param onclick JavaScript onclick event handler code
* @param align Optional HTML alignment value
*
* @return Populated <code>img</code> tag
*/
public static String buildImageTag(FacesContext context, String image, int width, int height, String alt, String onclick, String align)
{
StringBuilder buf = new StringBuilder(200);
buf.append("<img src=\"")
.append(context.getExternalContext().getRequestContextPath())
.append(image)
.append("\" width=")
.append(width)
.append(" height=")
.append(height)
.append(" border=0");
if (alt != null)
{
alt = Utils.encode(alt);
buf.append(" alt=\"")
.append(alt)
.append("\" title=\"")
.append(alt)
.append('"');
}
if (align != null)
{
buf.append(" align=")
.append(align);
}
if (onclick != null)
{
buf.append(" onclick=\"")
.append(onclick)
.append("\" style='cursor:pointer'");
}
buf.append('>');
return buf.toString();
}
/**
* Build a context path safe image tag for the supplied image path.
* Image path should be supplied with a leading slash '/'.
*
* @param context FacesContext
* @param image The local image path from the web folder with leading slash '/'
* @param width Width in pixels
* @param height Height in pixels
* @param alt Optional alt/title text
*
* @return Populated <code>img</code> tag
*/
public static String buildImageTag(FacesContext context, String image, int width, int height, String alt)
{
return buildImageTag(context, image, width, height, alt, null);
}
/**
* Build a context path safe image tag for the supplied image path.
* Image path should be supplied with a leading slash '/'.
*
* @param context FacesContext
* @param image The local image path from the web folder with leading slash '/'
* @param alt Optional alt/title text
*
* @return Populated <code>img</code> tag
*/
public static String buildImageTag(FacesContext context, String image, String alt)
{
return buildImageTag(context, image, alt, null);
}
/**
* Build a context path safe image tag for the supplied image path.
* Image path should be supplied with a leading slash '/'.
*
* @param context FacesContext
* @param image The local image path from the web folder with leading slash '/'
* @param alt Optional alt/title text
* @param align Optional HTML alignment value
*
* @return Populated <code>img</code> tag
*/
public static String buildImageTag(FacesContext context, String image, String alt, String align)
{
StringBuilder buf = new StringBuilder(128);
buf.append("<img src=\"")
.append(context.getExternalContext().getRequestContextPath())
.append(image)
.append("\" border=0");
if (alt != null)
{
alt = Utils.encode(alt);
buf.append(" alt=\"")
.append(alt)
.append("\" title=\"")
.append(alt)
.append('"');
}
if (align != null)
{
buf.append(" align=")
.append(align);
}
buf.append('>');
return buf.toString();
}
/**
* Return the parent UIForm component for the specified UIComponent
*
* @param context FaceContext
* @param component The UIComponent to find parent Form for
*
* @return UIForm parent or null if none found in hiearachy
*/
public static UIForm getParentForm(FacesContext context, UIComponent component)
{
UIComponent parent = component.getParent();
while (parent != null)
{
if (parent instanceof UIForm)
{
break;
}
parent = parent.getParent();
}
return (UIForm)parent;
}
/**
* Return the parent UIComponent implementing the NamingContainer interface for
* the specified UIComponent.
*
* @param context FaceContext
* @param component The UIComponent to find parent Form for
*
* @return NamingContainer parent or null if none found in hiearachy
*/
public static UIComponent getParentNamingContainer(FacesContext context, UIComponent component)
{
UIComponent parent = component.getParent();
while (parent != null)
{
if (parent instanceof NamingContainer)
{
break;
}
parent = parent.getParent();
}
return (UIComponent)parent;
}
/**
* Return the parent UIComponent implementing the IDataContainer interface for
* the specified UIComponent.
*
* @param context FaceContext
* @param component The UIComponent to find parent IDataContainer for
*
* @return IDataContainer parent or null if none found in hiearachy
*/
public static IDataContainer getParentDataContainer(FacesContext context, UIComponent component)
{
UIComponent parent = component.getParent();
while (parent != null)
{
if (parent instanceof IDataContainer)
{
break;
}
parent = parent.getParent();
}
return (IDataContainer)parent;
}
/**
* Determines whether the given component is disabled or readonly
*
* @param component The component to test
* @return true if the component is either disabled or set to readonly
*/
public static boolean isComponentDisabledOrReadOnly(UIComponent component)
{
boolean disabled = false;
boolean readOnly = false;
Object disabledAttr = component.getAttributes().get("disabled");
if (disabledAttr != null)
{
disabled = disabledAttr.equals(Boolean.TRUE);
}
if (disabled == false)
{
Object readOnlyAttr = component.getAttributes().get("disabled");
if (readOnlyAttr != null)
{
readOnly = readOnlyAttr.equals(Boolean.TRUE);
}
}
return disabled || readOnly;
}
/**
* Invoke the method encapsulated by the supplied MethodBinding
*
* @param context FacesContext
* @param method MethodBinding to invoke
* @param event ActionEvent to pass to the method of signature:
* public void myMethodName(ActionEvent event)
*/
public static void processActionMethod(FacesContext context, MethodBinding method, ActionEvent event)
{
try
{
method.invoke(context, new Object[] {event});
}
catch (EvaluationException e)
{
Throwable cause = e.getCause();
if (cause instanceof AbortProcessingException)
{
throw (AbortProcessingException)cause;
}
else
{
throw e;
}
}
}
/**
* Adds a global error message
*
* @param msg The error message
*/
public static void addErrorMessage(String msg)
{
addErrorMessage(msg, null);
}
/**
* Adds a global error message and logs exception details
*
* @param msg The error message
* @param err The exception to log
*/
public static void addErrorMessage(String msg, Throwable err)
{
FacesContext context = FacesContext.getCurrentInstance( );
FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg);
context.addMessage(null, facesMsg);
if (err != null)
{
if ((err instanceof InvalidNodeRefException == false &&
err instanceof AccessDeniedException == false) || logger.isDebugEnabled())
{
logger.error(msg, err);
}
}
}
/**
* Adds a global status message that will be displayed by a Status Message UI component
*
* @param severity Severity of the message
* @param msg Text of the message
*/
public static void addStatusMessage(FacesMessage.Severity severity, String msg)
{
FacesContext fc = FacesContext.getCurrentInstance();
String time = getTimeFormat(fc).format(new Date(System.currentTimeMillis()));
FacesMessage fm = new FacesMessage(severity, time, msg);
fc.addMessage(UIStatusMessage.STATUS_MESSAGE, fm);
}
/**
* @return the formatter for locale sensitive Time formatting
*/
public static DateFormat getTimeFormat(FacesContext fc)
{
return getDateFormatFromPattern(fc, Application.getMessage(fc, MSG_TIME_PATTERN));
}
/**
* @return the formatter for locale sensitive Date formatting
*/
public static DateFormat getDateFormat(FacesContext fc)
{
return getDateFormatFromPattern(fc, Application.getMessage(fc, MSG_DATE_PATTERN));
}
/**
* @return the formatter for locale sensitive Date & Time formatting
*/
public static DateFormat getDateTimeFormat(FacesContext fc)
{
return getDateFormatFromPattern(fc, Application.getMessage(fc, MSG_DATE_TIME_PATTERN));
}
/**
* @return DataFormat object for the specified pattern
*/
private static DateFormat getDateFormatFromPattern(FacesContext fc, String pattern)
{
if (pattern == null)
{
throw new IllegalArgumentException("DateTime pattern is mandatory.");
}
try
{
return new SimpleDateFormat(pattern, Application.getLanguage(fc));
}
catch (IllegalArgumentException err)
{
throw new AlfrescoRuntimeException("Invalid DateTime pattern", err);
}
}
/**
* Return the image path to the filetype icon for the specified file name string
*
* @param name File name to build filetype icon path for
* @param small True for the small 16x16 icon or false for the large 32x32
*
* @return the image path for the specified node type or the default icon if not found
*/
public static String getFileTypeImage(String name, boolean small)
{
String image = (small ? DEFAULT_FILE_IMAGE16 : DEFAULT_FILE_IMAGE32);
int extIndex = name.lastIndexOf('.');
if (extIndex != -1 && name.length() > extIndex + 1)
{
String ext = name.substring(extIndex + 1).toLowerCase();
String key = ext + ' ' + (small ? "16" : "32");
// found file extension for appropriate size image
synchronized (s_fileExtensionMap)
{
image = s_fileExtensionMap.get(key);
if (image == null)
{
// not found create for first time
image = (small ? IMAGE_PREFIX16 : IMAGE_PREFIX32) + ext + IMAGE_POSTFIX;
// does this image exist on the web-server?
if (FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(image) != null)
{
// found the image for this extension - save it for later
s_fileExtensionMap.put(key, image);
}
else
{
// not found, save the default image for this extension instead
image = (small ? DEFAULT_FILE_IMAGE16 : DEFAULT_FILE_IMAGE32);
s_fileExtensionMap.put(key, image);
}
}
}
}
return image;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.web.ui.common;
/**
* Class containing well known web resource paths for images etc.
*
* @author Kevin Roast
*/
public class WebResources
{
// Image paths
public static final String IMAGE_PREVIOUSPAGE_NONE = "/images/icons/PreviousPage_unavailable.gif";
public static final String IMAGE_PREVIOUSPAGE = "/images/icons/PreviousPage.gif";
public static final String IMAGE_FIRSTPAGE_NONE = "/images/icons/FirstPage_unavailable.gif";
public static final String IMAGE_FIRSTPAGE = "/images/icons/FirstPage.gif";
public static final String IMAGE_NEXTPAGE_NONE = "/images/icons/NextPage_unavailable.gif";
public static final String IMAGE_NEXTPAGE = "/images/icons/NextPage.gif";
public static final String IMAGE_LASTPAGE_NONE = "/images/icons/LastPage_unavailable.gif";
public static final String IMAGE_LASTPAGE = "/images/icons/LastPage.gif";
public static final String IMAGE_SORTUP = "/images/icons/sort_up.gif";
public static final String IMAGE_SORTDOWN = "/images/icons/sort_down.gif";
public static final String IMAGE_SORTNONE = "/images/icons/sort_flat.gif";
public static final String IMAGE_EXPANDED = "/images/icons/expanded.gif";
public static final String IMAGE_COLLAPSED = "/images/icons/collapsed.gif";
public static final String IMAGE_MOVELEFT = "/images/icons/move_left.gif";
public static final String IMAGE_MOVERIGHT = "/images/icons/move_right.gif";
public static final String IMAGE_GO_UP = "/images/icons/up.gif";
public static final String IMAGE_INFO = "/images/icons/info_icon.gif";
}

Some files were not shown because too many files have changed in this diff Show More