. First pass of the Saved Searches functionality for the web-client

. Saved Searches bootstrap folder
. Document details and Space details page now show a copy-and-paste NodeRef link 
. Minor bug fixes to Breadcrumb and ActionLink components

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2104 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2006-01-12 13:40:20 +00:00
parent 5d9576883d
commit 2652d33f08
21 changed files with 1202 additions and 90 deletions

View File

@@ -68,6 +68,7 @@ public class Application
private static String glossaryFolderName;
private static String spaceTemplatesFolderName;
private static String contentTemplatesFolderName;
private static String savedSearchesFolderName;
/**
* Private constructor to prevent instantiation of this class
@@ -261,7 +262,7 @@ public class Application
}
/**
* @return Returns the root path for the application (retrieved from config service)
* @return Returns the root path for the application
*/
public static String getRootPath(ServletContext context)
{
@@ -269,7 +270,7 @@ public class Application
}
/**
* @return Returns the root path for the application (retrieved from config service)
* @return Returns the root path for the application
*/
public static String getRootPath(FacesContext context)
{
@@ -277,7 +278,7 @@ public class Application
}
/**
* @return Returns the glossary folder name (retrieved from config service)
* @return Returns the glossary folder name
*/
public static String getGlossaryFolderName(ServletContext context)
{
@@ -285,7 +286,7 @@ public class Application
}
/**
* @return Returns the glossary folder name (retrieved from config service)
* @return Returns the glossary folder name
*/
public static String getGlossaryFolderName(FacesContext context)
{
@@ -293,7 +294,7 @@ public class Application
}
/**
* @return Returns the Space templates folder name (retrieved from config service)
* @return Returns the Space templates folder name
*/
public static String getSpaceTemplatesFolderName(ServletContext context)
{
@@ -301,7 +302,7 @@ public class Application
}
/**
* @return Returns the Space templates folder name (retrieved from config service)
* @return Returns the Space templates folder name
*/
public static String getSpaceTemplatesFolderName(FacesContext context)
{
@@ -309,7 +310,7 @@ public class Application
}
/**
* @return Returns the Content templates folder name (retrieved from config service)
* @return Returns the Content templates folder name
*/
public static String getContentTemplatesFolderName(ServletContext context)
{
@@ -317,13 +318,29 @@ public class Application
}
/**
* @return Returns the Content templates folder name (retrieved from config service)
* @return Returns the Content templates folder name
*/
public static String getContentTemplatesFolderName(FacesContext context)
{
return getContentTemplatesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Return the Saved Searches folder name
*/
public static String getSavedSearchesFolderName(ServletContext context)
{
return getSavedSearchesFolderName(WebApplicationContextUtils.getRequiredWebApplicationContext(context));
}
/**
* @return Return the Saved Searches folder name
*/
public static String getSavedSearchesFolderName(FacesContext context)
{
return getSavedSearchesFolderName(FacesContextUtils.getRequiredWebApplicationContext(context));
}
/**
* Set the language locale for the current user context
*
@@ -530,7 +547,7 @@ public class Application
}
/**
* Returns the repository store URL (retrieved from config service)
* Returns the repository store URL
*
* @param context The spring context
* @return The repository store URL to use
@@ -547,7 +564,7 @@ public class Application
}
/**
* Returns the root path for the application (retrieved from config service)
* Returns the root path for the application
*
* @param context The spring context
* @return The application root path
@@ -565,7 +582,7 @@ public class Application
}
/**
* Returns the glossary folder name (retrieved from config service)
* Returns the glossary folder name
*
* @param context The spring context
* @return The glossary folder name
@@ -583,7 +600,7 @@ public class Application
}
/**
* Returns the Space Templates folder name (retrieved from config service)
* Returns the Space Templates folder name
*
* @param context The spring context
* @return The templates folder name
@@ -601,7 +618,7 @@ public class Application
}
/**
* Returns the Content Templates folder name (retrieved from config service)
* Returns the Content Templates folder name
*
* @param context The spring context
* @return The templates folder name
@@ -618,6 +635,24 @@ public class Application
return contentTemplatesFolderName;
}
/**
* Returns the Saved Searches folder name
*
* @param context The spring context
* @return The saved searches folder name
*/
private static String getSavedSearchesFolderName(WebApplicationContext context)
{
if (savedSearchesFolderName == null)
{
ImporterBootstrap bootstrap = (ImporterBootstrap)context.getBean(BEAN_IMPORTER_BOOTSTRAP);
Properties configuration = bootstrap.getConfiguration();
savedSearchesFolderName = configuration.getProperty("spaces.savedsearches.childname");
}
return savedSearchesFolderName;
}
/**
* Retrieves the configured error page for the application
*

View File

@@ -16,6 +16,8 @@
*/
package org.alfresco.web.bean;
import java.io.Serializable;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -26,25 +28,36 @@ import java.util.Map;
import javax.faces.component.UISelectBoolean;
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.model.SelectItem;
import javax.transaction.UserTransaction;
import org.alfresco.config.ConfigService;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.permissions.AccessDeniedException;
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.ChildAssociationRef;
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.MimetypeService;
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.PermissionService;
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.SearchContext.RangeProperties;
import org.alfresco.web.bean.repository.MapNode;
import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
@@ -52,6 +65,7 @@ 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.Utils;
import org.alfresco.web.ui.common.component.UIPanel.ExpandedEvent;
import org.alfresco.web.ui.repo.component.UICategorySelector;
import org.alfresco.web.ui.repo.component.UISearchCustomProperties;
@@ -73,9 +87,9 @@ public class AdvancedSearchBean
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);
panels.put(PANEL_CATEGORIES, false);
panels.put(PANEL_ATTRS, false);
panels.put(PANEL_CUSTOM, false);
}
@@ -106,6 +120,14 @@ public class AdvancedSearchBean
this.namespaceService = namespaceService;
}
/**
* @param searchService the search service
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @return Returns the progressive panels expanded state map.
*/
@@ -122,6 +144,70 @@ public class AdvancedSearchBean
this.panels = panels;
}
/**
* @return Returns the saved search Description.
*/
public String getSearchDescription()
{
return this.searchDescription;
}
/**
* @param searchDescription The saved search Description to set.
*/
public void setSearchDescription(String searchDescription)
{
this.searchDescription = searchDescription;
}
/**
* @return Returns the saved search Name.
*/
public String getSearchName()
{
return this.searchName;
}
/**
* @param searchName The saved search Name to set.
*/
public void setSearchName(String searchName)
{
this.searchName = searchName;
}
/**
* @return ID of the last saved search selected by the user
*/
public String getSavedSearch()
{
return this.savedSearch;
}
/**
* @param savedSearch ID of the saved search selected by the user
*/
public void setSavedSearch(String savedSearch)
{
this.savedSearch = savedSearch;
}
/**
* @return name of the saved search to edit
*/
public String getEditSearchName()
{
return this.editSearchName;
}
/**
* @param editSearchName name of the saved search to edit
*/
public void setEditSearchName(String editSearchName)
{
this.editSearchName = editSearchName;
}
/**
* @return Returns the folder to search, null for all.
*/
@@ -496,19 +582,28 @@ public class AdvancedSearchBean
* Handler to clear the advanced search screen form details
*/
public void reset(ActionEvent event)
{
resetFields();
this.savedSearch = null;
}
private void resetFields()
{
this.text = "";
this.mode = MODE_ALL;
this.lookin = LOOKIN_ALL;
this.contentType = null;
this.contentFormat = null;
this.location = null;
this.locationChildren = false;
this.locationChildren = true;
this.categories = new ArrayList<Node>(2);
this.title = null;
this.description = null;
this.author = null;
this.createdDateFrom = null;
this.createdDateTo = null;
this.modifiedDateFrom = null;
this.modifiedDateTo = null;
this.createdDateChecked = false;
this.modifiedDateChecked = false;
this.customProperties.clear();
@@ -519,8 +614,6 @@ public class AdvancedSearchBean
*/
public String search()
{
String outcome = null;
if (this.text != null && this.text.length() != 0)
{
// construct the Search Context and set on the navigation bean
@@ -637,8 +730,7 @@ public class AdvancedSearchBean
// 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));
search.setLocation(SearchContext.getPathFromSpaceRef(this.location, this.locationChildren));
}
// category path search
@@ -648,7 +740,7 @@ public class AdvancedSearchBean
for (int i=0; i<paths.length; i++)
{
Node category = this.categories.get(i);
boolean includeChildren = (Boolean)category.getProperties().get("includeChildren");
boolean includeChildren = (Boolean)category.getProperties().get(INCLUDE_CHILDREN);
paths[i] = SearchContext.getPathFromSpaceRef(category.getNodeRef(), includeChildren);
}
search.setCategories(paths);
@@ -663,13 +755,425 @@ public class AdvancedSearchBean
// 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 "browse";
}
/**
* Action handler called to initiate the saved search screen
*/
public String saveSearch()
{
this.searchDescription = null;
this.searchName = null;
this.editSearchName = null;
if (this.savedSearch != null && NO_SELECTION.equals(this.savedSearch) == false)
{
// load previous for overwrite
try
{
NodeRef searchRef = new NodeRef(Repository.getStoreRef(), this.savedSearch);
Node searchNode = new Node(searchRef);
if (this.nodeService.exists(searchRef) && searchNode.hasPermission(PermissionService.WRITE))
{
Node node = new Node(searchRef);
this.searchName = node.getName();
this.editSearchName = this.searchName;
this.searchDescription = (String)node.getProperties().get(ContentModel.PROP_DESCRIPTION);
}
else
{
// unable to overwrite existing saved search
this.savedSearch = null;
}
}
catch (Throwable err)
{
// unable to overwrite existing saved search for some other reason
this.savedSearch = null;
}
}
return "saveSearch";
}
/**
* Action handler called to save the current search
*/
public String saveSearchOK()
{
String outcome = "browse";
NodeRef searchesRef = getSavedSearchesRef();
SearchContext search = this.navigator.getSearchContext();
if (searchesRef != null && search != null)
{
UserTransaction tx = null;
try
{
FacesContext context = FacesContext.getCurrentInstance();
tx = Repository.getUserTransaction(context);
tx.begin();
Map<QName, Serializable> props = null;
// handle Edit e.g. Overwrite of existing search
// detect if was previously selected saved search (e.g. NodeRef not null)
boolean edit = false;
if (this.savedSearch != null && NO_SELECTION.equals(this.savedSearch) == false)
{
NodeRef searchRef = new NodeRef(Repository.getStoreRef(), this.savedSearch);
edit = (this.nodeService.exists(searchRef));
}
ContentService contentService = Repository.getServiceRegistry(context).getContentService();
ContentWriter writer;
if (edit)
{
// edit existing saved search
NodeRef searchRef = new NodeRef(Repository.getStoreRef(), this.savedSearch);
props = this.nodeService.getProperties(searchRef);
props.put(ContentModel.PROP_NAME, this.searchName);
props.put(ContentModel.PROP_DESCRIPTION, this.searchDescription);
this.nodeService.setProperties(searchRef, props);
writer = contentService.getWriter(searchRef, ContentModel.PROP_CONTENT, true);
}
else
{
// create new content node as the saved search object
props = new HashMap<QName, Serializable>(2, 1.0f);
props.put(ContentModel.PROP_NAME, this.searchName);
props.put(ContentModel.PROP_DESCRIPTION, this.searchDescription);
ChildAssociationRef childRef = this.nodeService.createNode(
searchesRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.ALFRESCO_URI, QName.createValidLocalName(this.searchName)),
ContentModel.TYPE_CONTENT,
props);
writer = contentService.getWriter(childRef.getChildRef(), ContentModel.PROP_CONTENT, true);
}
// get a writer to our new node ready for XML content
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
writer.setEncoding("UTF-8");
// output an XML serialized version of the SearchContext object
writer.putContent(search.toXML());
tx.commit();
this.savedSearch = null;
}
catch (Throwable e)
{
try { if (tx != null) {tx.rollback();} } catch (Exception ex) {}
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_SAVE_SEARCH), e.getMessage()), e);
outcome = null;
}
}
return outcome;
}
/**
* @return list of saved searches as SelectItem objects
*/
public List<SelectItem> getSavedSearches()
{
// TODO: cache for 1 minute? - dirty cache when new search is saved!
FacesContext fc = FacesContext.getCurrentInstance();
String xpath = ".//*";
ServiceRegistry services = Repository.getServiceRegistry(fc);
List<SelectItem> savedSearches = null;
NodeRef searchesRef = getSavedSearchesRef();
if (searchesRef != null)
{
List<NodeRef> results = searchService.selectNodes(
searchesRef,
xpath,
null,
namespaceService,
false);
savedSearches = new ArrayList<SelectItem>(results.size() + 1);
if (results.size() != 0)
{
DictionaryService dd = services.getDictionaryService();
for (NodeRef ref : results)
{
Node childNode = new Node(ref);
if (dd.isSubClass(childNode.getType(), ContentModel.TYPE_CONTENT))
{
savedSearches.add(new SelectItem(childNode.getId(), childNode.getName()));
}
}
// make sure the list is sorted by the label
QuickSort sorter = new QuickSort(savedSearches, "label", true, IDataContainer.SORT_CASEINSENSITIVE);
sorter.sort();
}
}
else
{
// handle missing folder case
savedSearches = new ArrayList<SelectItem>(1);
}
// add an entry (at the start) to instruct the user to select a saved search
savedSearches.add(0, new SelectItem(NO_SELECTION,
Application.getMessage(FacesContext.getCurrentInstance(), MSG_SELECT_SAVED_SEARCH)));
return savedSearches;
}
/**
* Action handler called when a saved search is selected by the user
*/
public void selectSearch(ActionEvent event)
{
if (NO_SELECTION.equals(savedSearch) == false)
{
// read an XML serialized version of the SearchContext object
NodeRef searchSearchRef = new NodeRef(Repository.getStoreRef(), savedSearch);
ServiceRegistry services = Repository.getServiceRegistry(FacesContext.getCurrentInstance());
ContentService cs = services.getContentService();
try
{
if (services.getNodeService().exists(searchSearchRef))
{
ContentReader reader = cs.getReader(searchSearchRef, ContentModel.PROP_CONTENT);
SearchContext search = new SearchContext().fromXML(reader.getContentString());
// if we get here we read the serialized object successfully
// now setup the UI to match the new SearchContext object
initialiseFromContext(search);
}
}
catch (Throwable err)
{
Utils.addErrorMessage(MessageFormat.format(Application.getMessage(
FacesContext.getCurrentInstance(), MSG_ERROR_RESTORE_SEARCH), err.getMessage()), err);
}
}
}
/**
* Initialise the Advanced Search UI screen from a SearchContext
*
* @param search the SearchContext to retrieve state from
*/
private void initialiseFromContext(SearchContext search)
{
resetFields();
this.text = search.getText();
switch (search.getMode())
{
case SearchContext.SEARCH_ALL:
this.mode = MODE_ALL;
break;
case SearchContext.SEARCH_FILE_NAMES_CONTENTS:
this.mode = MODE_FILES_TEXT;
break;
case SearchContext.SEARCH_FILE_NAMES:
this.mode = MODE_FILES;
break;
case SearchContext.SEARCH_SPACE_NAMES:
this.mode = MODE_FOLDERS;
break;
}
this.panels.put(PANEL_RESTRICT, true);
if (search.getLocation() != null)
{
this.locationChildren = search.getLocation().endsWith("//*");
this.location = findNodeRefFromPath(search.getLocation());
this.lookin = LOOKIN_OTHER;
this.panels.put(PANEL_LOCATION, true);
}
String[] categories = search.getCategories();
if (categories != null && categories.length != 0)
{
for (String category : categories)
{
NodeRef categoryRef = findNodeRefFromPath(category);
if (categoryRef != null)
{
Node categoryNode = new MapNode(categoryRef);
// add a value bound propery used to indicate if searching across children is selected
categoryNode.getProperties().put(INCLUDE_CHILDREN, category.endsWith("//*"));
this.categories.add(categoryNode);
}
}
this.panels.put(PANEL_CATEGORIES, true);
}
this.contentType = search.getContentType();
this.contentFormat = search.getMimeType();
this.description = search.getAttributeQuery(ContentModel.PROP_DESCRIPTION);
this.title = search.getAttributeQuery(ContentModel.PROP_TITLE);
this.author = search.getAttributeQuery(ContentModel.PROP_AUTHOR);
if (this.contentType != null || this.contentFormat != null ||
this.description != null || this.title != null || this.author != null)
{
this.panels.put(PANEL_ATTRS, true);
}
RangeProperties createdDate = search.getRangeProperty(ContentModel.PROP_CREATED);
if (createdDate != null)
{
this.createdDateFrom = Utils.parseXMLDateFormat(createdDate.lower);
this.createdDateTo = Utils.parseXMLDateFormat(createdDate.upper);
this.createdDateChecked = true;
this.panels.put(PANEL_ATTRS, true);
}
RangeProperties modifiedDate = search.getRangeProperty(ContentModel.PROP_MODIFIED);
if (modifiedDate != null)
{
this.modifiedDateFrom = Utils.parseXMLDateFormat(modifiedDate.lower);
this.modifiedDateTo = Utils.parseXMLDateFormat(modifiedDate.upper);
this.modifiedDateChecked = true;
this.panels.put(PANEL_ATTRS, true);
}
// custom fields - calculate which are required to set through the custom properties lookup table
for (String qname : getCustomPropertyLookup().keySet())
{
DataTypeDefinition typeDef = getCustomPropertyLookup().get(qname);
if (typeDef != null)
{
QName typeName = typeDef.getName();
if (DataTypeDefinition.DATE.equals(typeName) || DataTypeDefinition.DATETIME.equals(typeName))
{
RangeProperties dateProps = search.getRangeProperty(QName.createQName(qname));
if (dateProps != null)
{
this.customProperties.put(UISearchCustomProperties.PREFIX_DATE_FROM + qname,
Utils.parseXMLDateFormat(dateProps.lower));
this.customProperties.put(UISearchCustomProperties.PREFIX_DATE_TO + qname,
Utils.parseXMLDateFormat(dateProps.upper));
this.customProperties.put(qname, true);
this.panels.put(PANEL_CUSTOM, true);
}
}
else if (DataTypeDefinition.BOOLEAN.equals(typeName))
{
String strBool = search.getFixedValueQuery(QName.createQName(qname));
if (strBool != null)
{
this.customProperties.put(qname, Boolean.parseBoolean(strBool));
this.panels.put(PANEL_CUSTOM, true);
}
}
else if (DataTypeDefinition.NODE_REF.equals(typeName) || DataTypeDefinition.CATEGORY.equals(typeName))
{
String strNodeRef = search.getFixedValueQuery(QName.createQName(qname));
if (strNodeRef != null)
{
this.customProperties.put(qname, new NodeRef(strNodeRef));
this.panels.put(PANEL_CUSTOM, true);
}
}
else if (DataTypeDefinition.INT.equals(typeName) || DataTypeDefinition.LONG.equals(typeName) ||
DataTypeDefinition.FLOAT.equals(typeName) || DataTypeDefinition.DOUBLE.equals(typeName))
{
// currently numbers are rendered as text in UISearchCustomProperties component
// this code will need updating if that changes!
this.customProperties.put(qname, search.getFixedValueQuery(QName.createQName(qname)));
this.panels.put(PANEL_CUSTOM, true);
}
else
{
this.customProperties.put(qname, search.getAttributeQuery(QName.createQName(qname)));
this.panels.put(PANEL_CUSTOM, true);
}
}
}
}
/**
* Return NodeRef to the last Node referenced on the end of the specified xpath value
*
* @param xpath XPath - note that any /* or //* will be removed to find trailing node
*
* @return NodeRef if found null otherwise
*/
private NodeRef findNodeRefFromPath(String xpath)
{
if (xpath.endsWith("//*"))
{
xpath = xpath.substring(0, xpath.lastIndexOf("//*"));
}
else if (xpath.endsWith("/*"))
{
xpath = xpath.substring(0, xpath.lastIndexOf("/*"));
}
NodeRef rootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
List<NodeRef> results = null;
try
{
results = searchService.selectNodes(
rootRef,
xpath,
null,
namespaceService,
false);
}
catch (AccessDeniedException err)
{
// ignore and return null
}
return (results != null && results.size() == 1) ? results.get(0) : null;
}
/**
* @return the cached reference to the shared Saved Searches folder
*/
private NodeRef getSavedSearchesRef()
{
if (savedSearchesRef == null)
{
FacesContext fc = FacesContext.getCurrentInstance();
String xpath = Application.getRootPath(fc) + "/" +
Application.getGlossaryFolderName(fc) + "/" +
Application.getSavedSearchesFolderName(fc);
NodeRef rootNodeRef = this.nodeService.getRootNode(Repository.getStoreRef());
List<NodeRef> results = null;
try
{
results = searchService.selectNodes(
rootNodeRef,
xpath,
null,
namespaceService,
false);
}
catch (AccessDeniedException err)
{
// ignore and return null
}
if (results != null && results.size() == 1)
{
savedSearchesRef = results.get(0);
}
}
return savedSearchesRef;
}
/**
* Action handler called when the Add button is pressed to add the current Category selection
*/
@@ -683,7 +1187,7 @@ public class AdvancedSearchBean
{
Node categoryNode = new MapNode(categoryRef);
// add a value bound propery used to indicate if searching across children is selected
categoryNode.getProperties().put("includeChildren", chkChildren.isSelected());
categoryNode.getProperties().put(INCLUDE_CHILDREN, chkChildren.isSelected());
this.categories.add(categoryNode);
}
}
@@ -768,6 +1272,17 @@ public class AdvancedSearchBean
private static final String MSG_CONTENT = "content";
private static final String MSG_ALL_FORMATS = "all_formats";
private static final String MSG_ERROR_SAVE_SEARCH = "error_save_search";
private static final String MSG_ERROR_RESTORE_SEARCH = "error_restore_search";
private static final String MSG_SELECT_SAVED_SEARCH = "select_saved_search";
private static final String PANEL_CUSTOM = "custom-panel";
private static final String PANEL_ATTRS = "attrs-panel";
private static final String PANEL_CATEGORIES = "categories-panel";
private static final String PANEL_RESTRICT = "restrict-panel";
private static final String PANEL_LOCATION = "location-panel";
private static final String INCLUDE_CHILDREN = "includeChildren";
private static final String MODE_ALL = "all";
private static final String MODE_FILES_TEXT = "files_text";
@@ -777,6 +1292,8 @@ public class AdvancedSearchBean
private static final String LOOKIN_ALL = "all";
private static final String LOOKIN_OTHER = "other";
private static final String NO_SELECTION = "none";
/** The NodeService to be used by the bean */
private NodeService nodeService;
@@ -786,12 +1303,19 @@ public class AdvancedSearchBean
/** The NavigationBean reference */
private NavigationBean navigator;
/** SearchService bean reference */
private SearchService searchService;
/** Client Config reference */
private ClientConfigElement clientConfigElement = null;
/** Progressive panel UI state */
private Map<String, Boolean> panels = new HashMap(5, 1.0f);
/** Saved search properties */
private String searchName;
private String searchDescription;
/** custom property names to values */
private Map<String, Object> customProperties = new HashMap(5, 1.0f);
@@ -854,4 +1378,10 @@ public class AdvancedSearchBean
private boolean modifiedDateChecked = false;
private boolean createdDateChecked = false;
private NodeRef savedSearchesRef = null;
private String savedSearch = null;
private String editSearchName = null;
}

View File

@@ -83,9 +83,6 @@ public class BrowseBean implements IContextListener
// ------------------------------------------------------------------------------
// Construction
private static final String VIEWMODE_DASHBOARD = "dashboard";
private static final String PAGE_NAME_BROWSE = "browse";
/**
* Default Constructor
*/
@@ -1443,6 +1440,9 @@ public class BrowseBean implements IContextListener
public static final String BROWSE_VIEW_ID = "/jsp/browse/browse.jsp";
private static final String VIEWMODE_DASHBOARD = "dashboard";
private static final String PAGE_NAME_BROWSE = "browse";
/** I18N messages */
private static final String MSG_ERROR_DELETE_FILE = "error_delete_file";
private static final String MSG_ERROR_DELETE_SPACE = "error_delete_space";

View File

@@ -198,6 +198,16 @@ public class DocumentDetailsBean
return Utils.generateURL(FacesContext.getCurrentInstance(), getDocument(), URLMode.CIFS);
}
/**
* Return the Alfresco NodeRef URL for the current document
*
* @return the Alfresco NodeRef URL
*/
public String getNodeRefUrl()
{
return getDocument().getNodeRef().toString();
}
/**
* Determines whether the current document is versionable
*

View File

@@ -17,13 +17,19 @@
package org.alfresco.web.bean;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.faces.context.FacesContext;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.impl.lucene.QueryParser;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -35,6 +41,12 @@ import org.alfresco.util.ISO9075;
import org.alfresco.web.bean.repository.Repository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**
* Holds the context required to build a search query and can return the populated query.
@@ -43,6 +55,28 @@ import org.apache.commons.logging.LogFactory;
*/
public final class SearchContext implements Serializable
{
private static final long serialVersionUID = 6730844584074229969L;
/** XML serialization elements */
private static final String ELEMENT_VALUE = "value";
private static final String ELEMENT_FIXED_VALUES = "fixed-values";
private static final String ELEMENT_INCLUSIVE = "inclusive";
private static final String ELEMENT_UPPER = "upper";
private static final String ELEMENT_LOWER = "lower";
private static final String ELEMENT_RANGE = "range";
private static final String ELEMENT_RANGES = "ranges";
private static final String ELEMENT_NAME = "name";
private static final String ELEMENT_ATTRIBUTE = "attribute";
private static final String ELEMENT_ATTRIBUTES = "attributes";
private static final String ELEMENT_MIMETYPE = "mimetype";
private static final String ELEMENT_CONTENT_TYPE = "content-type";
private static final String ELEMENT_CATEGORY = "category";
private static final String ELEMENT_CATEGORIES = "categories";
private static final String ELEMENT_LOCATION = "location";
private static final String ELEMENT_MODE = "mode";
private static final String ELEMENT_TEXT = "text";
private static final String ELEMENT_SEARCH = "search";
/** Search mode constants */
public final static int SEARCH_ALL = 0;
public final static int SEARCH_FILE_NAMES_CONTENTS = 1;
@@ -55,18 +89,12 @@ public final class SearchContext implements Serializable
/** mode for the search */
private int mode = SearchContext.SEARCH_ALL;
/** folder node location for the search */
/** folder XPath 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;
@@ -96,7 +124,7 @@ public final class SearchContext implements Serializable
String query;
// the QName for the well known "name" attribute
String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"));
String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, ELEMENT_NAME));
// match against content text
String text = this.text.trim();
@@ -375,7 +403,7 @@ public final class SearchContext implements Serializable
}
/**
* @return Returns the node to search from or null for all.
* @return Returns the node XPath to search in or null for all.
*/
public String getLocation()
{
@@ -383,7 +411,7 @@ public final class SearchContext implements Serializable
}
/**
* @param location The node to search from or null for all..
* @param location The node XPATH to search from or null for all..
*/
public void setLocation(String location)
{
@@ -452,38 +480,6 @@ public final class SearchContext implements Serializable
{
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
@@ -496,6 +492,11 @@ public final class SearchContext implements Serializable
this.queryAttributes.put(qname, value);
}
public String getAttributeQuery(QName qname)
{
return this.queryAttributes.get(qname);
}
/**
* Add an additional range attribute to search against
*
@@ -509,6 +510,11 @@ public final class SearchContext implements Serializable
this.rangeAttributes.put(qname, new RangeProperties(qname, lower, upper, inclusive));
}
public RangeProperties getRangeProperty(QName qname)
{
return this.rangeAttributes.get(qname);
}
/**
* Add an additional fixed value attribute to search against
*
@@ -520,11 +526,216 @@ public final class SearchContext implements Serializable
this.queryFixedValues.put(qname, value);
}
public String getFixedValueQuery(QName qname)
{
return this.queryFixedValues.get(qname);
}
/**
* @return this SearchContext as XML
*
* Example:
* <code>
* <?xml version="1.0" encoding="UTF-8"?>
* <search>
* <text>CDATA</text>
* <mode>int</mode>
* <location>XPath</location>
* <categories>
* <category>XPath</category>
* </categories>
* <content-type>String</content-type>
* <mimetype>String</mimetype>
* <attributes>
* <attribute name="String">String</attribute>
* </attributes>
* <ranges>
* <range name="String">
* <lower>String</lower>
* <upper>String</upper>
* <inclusive>boolean</inclusive>
* </range>
* </ranges>
* <fixed-values>
* <value name="String">String</value>
* </fixed-values>
* </search>
* </code>
*/
public String toXML()
{
try
{
NamespaceService ns = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNamespaceService();
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement(ELEMENT_SEARCH);
root.addElement(ELEMENT_TEXT).addCDATA(this.text);
root.addElement(ELEMENT_MODE).addText(Integer.toString(this.mode));
if (this.location != null)
{
root.addElement(ELEMENT_LOCATION).addText(this.location);
}
Element categories = root.addElement(ELEMENT_CATEGORIES);
for (String path : this.categories)
{
categories.addElement(ELEMENT_CATEGORY).addText(path);
}
if (this.contentType != null)
{
root.addElement(ELEMENT_CONTENT_TYPE).addText(this.contentType);
}
if (this.mimeType != null)
{
root.addElement(ELEMENT_MIMETYPE).addText(this.mimeType);
}
Element attributes = root.addElement(ELEMENT_ATTRIBUTES);
for (QName attrName : this.queryAttributes.keySet())
{
attributes.addElement(ELEMENT_ATTRIBUTE)
.addAttribute(ELEMENT_NAME, attrName.toPrefixString(ns))
.addCDATA(this.queryAttributes.get(attrName));
}
Element ranges = root.addElement(ELEMENT_RANGES);
for (QName rangeName : this.rangeAttributes.keySet())
{
RangeProperties rangeProps = this.rangeAttributes.get(rangeName);
Element range = ranges.addElement(ELEMENT_RANGE);
range.addAttribute(ELEMENT_NAME, rangeName.toPrefixString(ns));
range.addElement(ELEMENT_LOWER).addText(rangeProps.lower);
range.addElement(ELEMENT_UPPER).addText(rangeProps.upper);
range.addElement(ELEMENT_INCLUSIVE).addText(Boolean.toString(rangeProps.inclusive));
}
Element values = root.addElement(ELEMENT_FIXED_VALUES);
for (QName valueName : this.queryFixedValues.keySet())
{
values.addElement(ELEMENT_VALUE)
.addAttribute(ELEMENT_NAME, valueName.toPrefixString(ns))
.addCDATA(this.queryFixedValues.get(valueName));
}
StringWriter out = new StringWriter(1024);
XMLWriter writer = new XMLWriter(OutputFormat.createPrettyPrint());
writer.setWriter(out);
writer.write(doc);
return out.toString();
}
catch (Throwable err)
{
throw new AlfrescoRuntimeException("Failed to export SearchContext to XML.", err);
}
}
/**
* Restore a SearchContext from an XML definition
*
* @param xml XML format SearchContext @see #toXML()
*/
public SearchContext fromXML(String xml)
{
try
{
NamespaceService ns = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNamespaceService();
// get the root element
SAXReader reader = new SAXReader();
Document document = reader.read(new StringReader(xml));
Element rootElement = document.getRootElement();
Element textElement = rootElement.element(ELEMENT_TEXT);
if (textElement != null)
{
this.text = textElement.getText();
}
Element modeElement = rootElement.element(ELEMENT_MODE);
if (modeElement != null)
{
this.mode = Integer.parseInt(modeElement.getText());
}
Element locationElement = rootElement.element(ELEMENT_LOCATION);
if (locationElement != null)
{
this.location = locationElement.getText();
}
Element categoriesElement = rootElement.element(ELEMENT_CATEGORIES);
if (categoriesElement != null)
{
List<String> categories = new ArrayList<String>(4);
for (Iterator i=categoriesElement.elementIterator(ELEMENT_CATEGORY); i.hasNext(); /**/)
{
Element categoryElement = (Element)i.next();
categories.add(categoryElement.getText());
}
this.categories = categories.toArray(this.categories);
}
Element contentTypeElement = rootElement.element(ELEMENT_CONTENT_TYPE);
if (contentTypeElement != null)
{
this.contentType = contentTypeElement.getText();
}
Element mimetypeElement = rootElement.element(ELEMENT_MIMETYPE);
if (mimetypeElement != null)
{
this.mimeType = mimetypeElement.getText();
}
Element attributesElement = rootElement.element(ELEMENT_ATTRIBUTES);
if (attributesElement != null)
{
for (Iterator i=attributesElement.elementIterator(ELEMENT_ATTRIBUTE); i.hasNext(); /**/)
{
Element attrElement = (Element)i.next();
QName qname = QName.createQName(attrElement.attributeValue(ELEMENT_NAME), ns);
addAttributeQuery(qname, attrElement.getText());
}
}
Element rangesElement = rootElement.element(ELEMENT_RANGES);
if (rangesElement != null)
{
for (Iterator i=rangesElement.elementIterator(ELEMENT_RANGE); i.hasNext(); /**/)
{
Element rangeElement = (Element)i.next();
Element lowerElement = rangeElement.element(ELEMENT_LOWER);
Element upperElement = rangeElement.element(ELEMENT_UPPER);
Element incElement = rangeElement.element(ELEMENT_INCLUSIVE);
if (lowerElement != null && upperElement != null && incElement != null)
{
QName qname = QName.createQName(rangeElement.attributeValue(ELEMENT_NAME), ns);
addRangeQuery(qname,
lowerElement.getText(), upperElement.getText(),
Boolean.parseBoolean(incElement.getText()));
}
}
}
Element valuesElement = rootElement.element(ELEMENT_FIXED_VALUES);
if (valuesElement != null)
{
for (Iterator i=valuesElement.elementIterator(ELEMENT_VALUE); i.hasNext(); /**/)
{
Element valueElement = (Element)i.next();
QName qname = QName.createQName(valueElement.attributeValue(ELEMENT_NAME), ns);
addFixedValueQuery(qname, valueElement.getText());
}
}
}
catch (Throwable err)
{
throw new AlfrescoRuntimeException("Failed to import SearchContext from XML.", err);
}
return this;
}
/**
* Simple wrapper class for range query attribute properties
*/
private static class RangeProperties
static class RangeProperties
{
QName qname;
String lower;

View File

@@ -117,7 +117,7 @@ public class SpaceDetailsBean
}
/**
* Returns the id of the current document
* Returns the id of the current space
*
* @return The id
*/
@@ -127,9 +127,9 @@ public class SpaceDetailsBean
}
/**
* Returns the name of the current document
* Returns the name of the current space
*
* @return Name of the current document
* @return Name of the current space
*/
public String getName()
{
@@ -137,7 +137,7 @@ public class SpaceDetailsBean
}
/**
* Returns the WebDAV URL for the current document
* Returns the WebDAV URL for the current space
*
* @return The WebDAV url
*/
@@ -147,7 +147,7 @@ public class SpaceDetailsBean
}
/**
* Returns the URL to access the details page for the current document
* Returns the URL to access the details page for the current space
*
* @return The bookmark URL
*/
@@ -157,7 +157,7 @@ public class SpaceDetailsBean
}
/**
* Returns the CIFS path for the current document
* Returns the CIFS path for the current space
*
* @return The CIFS path
*/
@@ -165,6 +165,16 @@ public class SpaceDetailsBean
{
return Utils.generateURL(FacesContext.getCurrentInstance(), getSpace(), URLMode.CIFS);
}
/**
* Return the Alfresco NodeRef URL for the current space
*
* @return the Alfresco NodeRef URL
*/
public String getNodeRefUrl()
{
return getSpace().getNodeRef().toString();
}
/**
* @return Returns the template Id.

View File

@@ -133,7 +133,6 @@ public abstract class BasePreviewBean
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) + "/" +
@@ -143,7 +142,7 @@ public abstract class BasePreviewBean
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());
List<SelectItem> templates = new ArrayList<SelectItem>(results.size() + 1);
if (results.size() != 0)
{
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();

View File

@@ -22,6 +22,7 @@ import java.io.StringReader;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@@ -987,6 +988,79 @@ public final class Utils
throw new AlfrescoRuntimeException("Invalid DateTime pattern", err);
}
}
/**
* Parse XML format date YYYY-MM-DDTHH:MM:SS
* @param isoDate
* @return Date or null if failed to parse
*/
public static Date parseXMLDateFormat(String isoDate)
{
Date parsed = null;
try
{
int offset = 0;
// extract year
int year = Integer.parseInt(isoDate.substring(offset, offset += 4));
if (isoDate.charAt(offset) != '-')
{
throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset));
}
// extract month
int month = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
if (isoDate.charAt(offset) != '-')
{
throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset));
}
// extract day
int day = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
if (isoDate.charAt(offset) != 'T')
{
throw new IndexOutOfBoundsException("Expected T character but found " + isoDate.charAt(offset));
}
// extract hours, minutes, seconds and milliseconds
int hour = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
if (isoDate.charAt(offset) != ':')
{
throw new IndexOutOfBoundsException("Expected : character but found " + isoDate.charAt(offset));
}
int minutes = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
if (isoDate.charAt(offset) != ':')
{
throw new IndexOutOfBoundsException("Expected : character but found " + isoDate.charAt(offset));
}
int seconds = Integer.parseInt(isoDate.substring(offset += 1 , offset += 2));
// initialize Calendar object
Calendar calendar = Calendar.getInstance();
calendar.setLenient(false);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minutes);
calendar.set(Calendar.SECOND, seconds);
// extract the date
parsed = calendar.getTime();
}
catch(IndexOutOfBoundsException e)
{
}
catch(NumberFormatException e)
{
}
catch(IllegalArgumentException e)
{
}
return parsed;
}
/**
* Return the image path to the filetype icon for the specified file name string

View File

@@ -303,6 +303,7 @@ public class UIActionLink extends UICommand
this.onclick = onclick;
}
// ------------------------------------------------------------------------------
// Private data

View File

@@ -158,10 +158,10 @@ public class ActionLinkRenderer extends BaseRenderer
linkBuf.append(" class=")
.append(attrs.get("styleClass"));
}
if (attrs.get("tooltip") != null)
if (link.getTooltip() != null)
{
linkBuf.append(" title=\"")
.append(Utils.encode((String)attrs.get("tooltip")))
.append(Utils.encode(link.getTooltip()))
.append('"');
}
linkBuf.append('>');
@@ -266,10 +266,8 @@ public class ActionLinkRenderer extends BaseRenderer
}
buf.append(">");
Map attrs = link.getAttributes();
// render text link cell for the menu
if (attrs.get("href") == null)
if (link.getHref() == null)
{
buf.append("<a href='#' onclick=\"");
buf.append(Utils.generateFormSubmit(context, link, Utils.getActionHiddenFieldName(context, link), link.getClientId(context), getParameterMap(link)));
@@ -277,7 +275,7 @@ public class ActionLinkRenderer extends BaseRenderer
}
else
{
String href = (String)attrs.get("href");
String href = link.getHref();
if (href.startsWith("http") == false)
{
href = context.getExternalContext().getRequestContextPath() + href;
@@ -295,6 +293,7 @@ public class ActionLinkRenderer extends BaseRenderer
}
}
Map attrs = link.getAttributes();
if (attrs.get("style") != null)
{
buf.append(" style=\"")

View File

@@ -108,7 +108,7 @@ public class BreadcrumbRenderer extends BaseRenderer
if (first == false)
{
buf.append(' ')
.append(bc.getSeparator())
.append(Utils.encode(bc.getSeparator()))
.append(' ');
}

View File

@@ -170,6 +170,10 @@
<from-outcome>showTopic</from-outcome>
<to-view-id>/jsp/forums/topic.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>saveSearch</from-outcome>
<to-view-id>/jsp/dialog/save-search.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<!-- Admin Console rules -->

View File

@@ -191,6 +191,10 @@
<property-name>namespaceService</property-name>
<value>#{NamespaceService}</value>
</managed-property>
<managed-property>
<property-name>searchService</property-name>
<value>#{SearchService}</value>
</managed-property>
</managed-bean>
<managed-bean>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -163,6 +163,7 @@
<%-- Current object actions --%>
<h:outputText style="padding-left:20px" styleClass="mainSubTitle" value="#{msg.actions}" id="msg14" /><br>
<a:actionLink value="#{msg.new_search}" image="/images/icons/search_icon.gif" padding="4" action="advSearch" id="link20" />
<a:actionLink value="#{msg.save_search}" image="/images/icons/save_search.gif" padding="4" action="#{AdvancedSearchBean.saveSearch}" id="link20_1" />
<a:actionLink value="#{msg.close_search}" image="/images/icons/action.gif" padding="4" actionListener="#{BrowseBean.closeSearch}" id="link21" />
</td>
</a:panel>
@@ -209,7 +210,7 @@
<%-- component to display if the list is empty --%>
<f:facet name="empty">
<%-- TODO: either build complete message in BrowseBean or have no icon... --%>
<h:outputFormat value="#{msg.no_space_items}" escape="false">
<h:outputFormat value="#{msg.no_space_items}" escape="false" rendered="#{NavigationBean.searchContext == null}">
<f:param value="#{msg.new_space}" />
</h:outputFormat>
</f:facet>
@@ -362,7 +363,7 @@
<%-- component to display if the list is empty --%>
<f:facet name="empty">
<%-- TODO: either build complete message in BrowseBean or have no icon... --%>
<h:outputFormat value="#{msg.no_content_items}" escape="false">
<h:outputFormat value="#{msg.no_content_items}" escape="false" rendered="#{NavigationBean.searchContext == null}">
<f:param value="#{msg.add_content}" />
<f:param value="#{msg.create_content}" />
</h:outputFormat>

View File

@@ -104,6 +104,21 @@
<h:outputText style="padding-left:20px" styleClass="mainSubTitle" value="#{msg.actions}" /><br>
<a:actionLink value="#{msg.resetall}" image="/images/icons/delete.gif" padding="4" actionListener="#{AdvancedSearchBean.reset}" />
</td>
<td bgcolor="#465F7D" width=1></td>
<td width=100 style="padding-left:2px">
<%-- Available Saved Searches --%>
<h:outputText style="padding-left:20px" styleClass="mainSubTitle" value="#{msg.saved_searches}" />
<div style="padding-top:4px;white-space:nowrap">
<%-- Saved Searches drop-down selector --%>
<%-- uses a nasty hack to execute an ActionListener for the drop-down
tried using a valueChangedListener+formsubmit but the valueChangedListener
is called too late in the lifecycle for the form controls to be managed --%>
<h:selectOneMenu id="searches" value="#{AdvancedSearchBean.savedSearch}" onchange="document.forms['advsearch']['advsearch:act'].value='advsearch:show-search'; document.forms['advsearch'].submit(); return true;">
<f:selectItems value="#{AdvancedSearchBean.savedSearches}" />
</h:selectOneMenu>
<div style="display:none"><a:actionLink id="show-search" value="Select" actionListener="#{AdvancedSearchBean.selectSearch}" /></div>
</div>
</td>
</tr>
</table>

View File

@@ -225,7 +225,10 @@
<a:actionLink value="#{msg.download_content}" href="#{DocumentDetailsBean.downloadUrl}" target="new" id="link4" />
</td>
<td>
<a:actionLink value="#{msg.details_page_bookmark}" href="#{DocumentDetailsBean.bookmarkUrl}" target="new" id="link5" />
<a href='<h:outputText value="#{DocumentDetailsBean.bookmarkUrl}" escape="false" />' onclick="return false;"><h:outputText value="#{msg.details_page_bookmark}" /></a>
</td>
<td>
<a href='<h:outputText value="#{DocumentDetailsBean.nodeRefUrl}" escape="false" />' onclick="return false;"><h:outputText value="#{msg.noderef_link}" /></a>
</td>
</tr>
</table>

View File

@@ -0,0 +1,203 @@
<%--
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.
--%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %>
<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %>
<%@ page isELIgnored="false" %>
<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %>
<r:page titleId="title_save_search">
<script language="JavaScript1.2">
window.onload = pageLoaded;
function pageLoaded()
{
document.getElementById("save-search:name").focus();
checkButtonState();
}
function checkButtonState()
{
if (document.getElementById("save-search:name").value.length == 0 )
{
document.getElementById("save-search:ok-button").disabled = true;
}
else
{
document.getElementById("save-search:ok-button").disabled = false;
}
}
</script>
<f:view>
<%-- load a bundle of properties with I18N strings --%>
<f:loadBundle basename="alfresco.messages.webclient" var="msg"/>
<h:form acceptCharset="UTF-8" id="save-search">
<%-- Main outer table --%>
<table cellspacing="0" cellpadding="2">
<%-- Title bar --%>
<tr>
<td colspan="2">
<%@ include file="../parts/titlebar.jsp" %>
</td>
</tr>
<%-- Main area --%>
<tr valign="top">
<%-- Shelf --%>
<td>
<%@ include file="../parts/shelf.jsp" %>
</td>
<%-- Work Area --%>
<td width="100%">
<table cellspacing="0" cellpadding="0" width="100%">
<%-- Breadcrumb --%>
<%@ include file="../parts/breadcrumb.jsp" %>
<%-- Status and Actions --%>
<tr>
<td style="background-image: url(<%=request.getContextPath()%>/images/parts/statuspanel_4.gif)" width="4"></td>
<td bgcolor="#EEEEEE">
<%-- Status and Actions inner contents table --%>
<%-- Generally this consists of an icon, textual summary and actions for the current object --%>
<table cellspacing="4" cellpadding="0" width="100%">
<tr valign="top">
<td width="32">
<h:graphicImage id="wizard-logo" url="/images/icons/save_search_large.gif" />
</td>
<td>
<div class="mainTitle"><h:outputText value="#{msg.save_search}" /></div>
<div class="mainSubText"><h:outputText value="#{msg.save_search_description}" /></div>
</td>
</tr>
</table>
</td>
<td style="background-image: url(<%=request.getContextPath()%>/images/parts/statuspanel_6.gif)" width="4"></td>
</tr>
<%-- separator row with gradient shadow --%>
<tr>
<td><img src="<%=request.getContextPath()%>/images/parts/statuspanel_7.gif" width="4" height="9"></td>
<td style="background-image: url(<%=request.getContextPath()%>/images/parts/statuspanel_8.gif)"></td>
<td><img src="<%=request.getContextPath()%>/images/parts/statuspanel_9.gif" width="4" height="9"></td>
</tr>
<%-- Details --%>
<tr valign=top>
<td style="background-image: url(<%=request.getContextPath()%>/images/parts/whitepanel_4.gif)" width="4"></td>
<td>
<table cellspacing="0" cellpadding="3" border="0" width="100%">
<tr>
<td width="100%" valign="top">
<a:errors message="#{msg.error_wizard}" styleClass="errorMessage" />
<% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %>
<table cellpadding="2" cellspacing="2" border="0" width="100%">
<a:panel id="edit-panel" rendered="#{AdvancedSearchBean.editSearchName != null}">
<tr>
<td width="100%" valign="top" colspan="2" style="padding-bottom:6px">
<% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc"); %>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td valign=top style="padding-top:2px" width=20><h:graphicImage url="/images/icons/info_icon.gif" width="16" height="16"/></td>
<td class="mainSubText">
<h:outputFormat value="#{msg.saved_search_warning}">
<f:param value="#{AdvancedSearchBean.editSearchName}" />
</h:outputFormat>
</td>
</tr>
</table>
<% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner"); %>
</td>
</tr>
</a:panel>
<tr>
<td colspan="2" class="wizardSectionHeading"><h:outputText value="#{msg.search_props}" /></td>
</tr>
<tr>
<td><h:outputText value="#{msg.name}" />:</td>
<td>
<h:inputText id="name" value="#{AdvancedSearchBean.searchName}" size="35" maxlength="1024"
onkeyup="javascript:checkButtonState();" onchange="javascript:checkButtonState();"/>&nbsp;*
</td>
</tr>
<tr>
<td><h:outputText value="#{msg.description}" />:</td>
<td>
<h:inputText value="#{AdvancedSearchBean.searchDescription}" size="35" maxlength="1024" />
</td>
</tr>
</table>
<% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %>
</td>
<td valign="top">
<% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %>
<table cellpadding="1" cellspacing="1" border="0">
<tr>
<td align="center">
<h:commandButton id="ok-button" value="#{msg.save}" action="#{AdvancedSearchBean.saveSearchOK}"
styleClass="wizardButton" disabled="true" />
</td>
</tr>
<tr>
<td align="center">
<h:commandButton value="#{msg.cancel}" action="browse" styleClass="wizardButton" />
</td>
</tr>
</table>
<% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %>
</td>
</tr>
</table>
</td>
<td style="background-image: url(<%=request.getContextPath()%>/images/parts/whitepanel_6.gif)" width="4"></td>
</tr>
<%-- separator row with bottom panel graphics --%>
<tr>
<td><img src="<%=request.getContextPath()%>/images/parts/whitepanel_7.gif" width="4" height="4"></td>
<td width="100%" align="center" style="background-image: url(<%=request.getContextPath()%>/images/parts/whitepanel_8.gif)"></td>
<td><img src="<%=request.getContextPath()%>/images/parts/whitepanel_9.gif" width="4" height="4"></td>
</tr>
</table>
</td>
</tr>
</table>
</h:form>
</f:view>
</r:page>

View File

@@ -225,7 +225,10 @@
<a:actionLink value="#{msg.view_in_cifs}" href="#{SpaceDetailsBean.cifsPath}" target="new" id="link2" />
</td>
<td>
<a:actionLink value="#{msg.details_page_bookmark}" href="#{SpaceDetailsBean.bookmarkUrl}" target="new" id="link3" />
<a href='<h:outputText value="#{SpaceDetailsBean.bookmarkUrl}" escape="false" />' onclick="return false;"><h:outputText value="#{msg.details_page_bookmark}" /></a>
</td>
<td>
<a href='<h:outputText value="#{SpaceDetailsBean.nodeRefUrl}" escape="false" />' onclick="return false;"><h:outputText value="#{msg.noderef_link}" /></a>
</td>
</tr>
</table>