diff options
Diffstat (limited to 'archiva-modules')
9 files changed, 671 insertions, 112 deletions
diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java index 63e21151c..52596ff9f 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/CrossRepositorySearch.java @@ -69,4 +69,21 @@ public interface CrossRepositorySearch * @return the results. */ public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum, SearchResultLimits limits ); + + /** + * Search for a specific artifact matching the given field values. The search is performed on the bytecode + * index/indices. + * + * @param principal + * @param selectedRepos repository to be searched + * @param groupId groupId to be matched + * @param artifactId artifactId to be matched + * @param version version to be matched + * @param className Java class or package name to be matched + * @param limits the limits to apply to the search results + * @return + */ + public SearchResults executeFilteredSearch( String principal, List<String> selectedRepos, String groupId, + String artifactId, String version, String className, + SearchResultLimits limits ); } diff --git a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java index ede311346..a21c4fa6b 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/main/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearch.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; import org.apache.lucene.document.Document; +import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.MultiFieldQueryParser; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; @@ -32,16 +33,20 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Filter; import org.apache.lucene.search.Hits; import org.apache.lucene.search.MultiSearcher; +import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryWrapperFilter; import org.apache.lucene.search.Searchable; +import org.apache.lucene.search.TermQuery; import org.apache.maven.archiva.configuration.ArchivaConfiguration; import org.apache.maven.archiva.configuration.ConfigurationNames; import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; +import org.apache.maven.archiva.indexer.ArtifactKeys; import org.apache.maven.archiva.indexer.RepositoryContentIndex; import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory; import org.apache.maven.archiva.indexer.RepositoryIndexException; import org.apache.maven.archiva.indexer.RepositoryIndexSearchException; import org.apache.maven.archiva.indexer.bytecode.BytecodeHandlers; +import org.apache.maven.archiva.indexer.bytecode.BytecodeKeys; import org.apache.maven.archiva.indexer.filecontent.FileContentHandlers; import org.apache.maven.archiva.indexer.hashcodes.HashcodesHandlers; import org.apache.maven.archiva.indexer.hashcodes.HashcodesKeys; @@ -57,7 +62,7 @@ import org.slf4j.LoggerFactory; /** * DefaultCrossRepositorySearch - * + * * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a> * @version $Id$ * @plexus.component role="org.apache.maven.archiva.indexer.search.CrossRepositorySearch" role-hint="default" @@ -66,12 +71,12 @@ public class DefaultCrossRepositorySearch implements CrossRepositorySearch, RegistryListener, Initializable { private Logger log = LoggerFactory.getLogger( DefaultCrossRepositorySearch.class ); - + /** * @plexus.requirement role-hint="lucene" */ private RepositoryContentIndexFactory indexFactory; - + /** * @plexus.requirement */ @@ -79,14 +84,61 @@ public class DefaultCrossRepositorySearch private List<ManagedRepositoryConfiguration> localIndexedRepositories = new ArrayList<ManagedRepositoryConfiguration>(); - public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum, SearchResultLimits limits ) + public SearchResults executeFilteredSearch( String principal, List<String> selectedRepos, String groupId, + String artifactId, String version, String className, + SearchResultLimits limits ) + { + List<RepositoryContentIndex> indexes = getBytecodeIndexes( principal, selectedRepos ); + SearchResults results = new SearchResults(); + BooleanQuery booleanQuery = new BooleanQuery(); + + if ( groupId != null && groupId.length() > 0 ) + { + parseAndAdd( booleanQuery, ArtifactKeys.GROUPID, groupId, "\\.|-" ); + } + + if ( artifactId != null && artifactId.length() > 0 ) + { + parseAndAdd( booleanQuery, ArtifactKeys.ARTIFACTID, artifactId, "\\.|-" ); + } + + if ( version != null && version.length() > 0 ) + { + parseAndAdd( booleanQuery, ArtifactKeys.VERSION, version, "\\.|-" ); + } + + if ( className != null && className.length() > 0 ) + { + + try + { + QueryParser parser = + new MultiFieldQueryParser( new String[] { BytecodeKeys.CLASSES, BytecodeKeys.FILES, + BytecodeKeys.METHODS }, new BytecodeHandlers().getAnalyzer() ); + booleanQuery.add( parser.parse( className ), BooleanClause.Occur.MUST ); + } + catch ( ParseException e ) + { + + } + } + + LuceneQuery query = new LuceneQuery( booleanQuery ); + results = searchAll( query, limits, indexes, null ); + results.getRepositories().add( this.localIndexedRepositories ); + + return results; + } + + public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum, + SearchResultLimits limits ) { List<RepositoryContentIndex> indexes = getHashcodeIndexes( principal, selectedRepos ); try { QueryParser parser = new MultiFieldQueryParser( new String[]{HashcodesKeys.MD5, HashcodesKeys.SHA1}, - new HashcodesHandlers().getAnalyzer() ); + new HashcodesHandlers().getAnalyzer() ); LuceneQuery query = new LuceneQuery( parser.parse( checksum ) ); SearchResults results = searchAll( query, limits, indexes, null ); results.getRepositories().addAll( this.localIndexedRepositories ); @@ -124,23 +176,22 @@ public class DefaultCrossRepositorySearch return new SearchResults(); } - public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits ) { return searchForTerm( principal, selectedRepos, term, limits, null ); } - + public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits, List<String> previousSearchTerms ) { List<RepositoryContentIndex> indexes = getFileContentIndexes( principal, selectedRepos ); - + try { QueryParser parser = new FileContentHandlers().getQueryParser(); LuceneQuery query = null; SearchResults results = null; - if( previousSearchTerms == null || previousSearchTerms.isEmpty() ) + if ( previousSearchTerms == null || previousSearchTerms.isEmpty() ) { query = new LuceneQuery( parser.parse( term ) ); results = searchAll( query, limits, indexes, null ); @@ -149,17 +200,17 @@ public class DefaultCrossRepositorySearch { // AND the previous search terms BooleanQuery booleanQuery = new BooleanQuery(); - for( String previousSearchTerm : previousSearchTerms ) + for ( String previousSearchTerm : previousSearchTerms ) { booleanQuery.add( parser.parse( previousSearchTerm ), BooleanClause.Occur.MUST ); } - - query = new LuceneQuery( booleanQuery ); + + query = new LuceneQuery( booleanQuery ); Filter filter = new QueryWrapperFilter( parser.parse( term ) ); results = searchAll( query, limits, indexes, filter ); - } + } results.getRepositories().addAll( this.localIndexedRepositories ); - + return results; } catch ( ParseException e ) @@ -168,9 +219,9 @@ public class DefaultCrossRepositorySearch } // empty results. - return new SearchResults(); + return new SearchResults(); } - + private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes, Filter filter ) { org.apache.lucene.search.Query specificQuery = luceneQuery.getLuceneQuery(); @@ -203,7 +254,7 @@ public class DefaultCrossRepositorySearch // Perform the search. Hits hits = null; - if( filter != null ) + if ( filter != null ) { hits = searcher.search( specificQuery, filter ); } @@ -355,7 +406,7 @@ public class DefaultCrossRepositorySearch return ret; } - + private boolean indexExists( RepositoryContentIndex index ) { try @@ -402,6 +453,26 @@ public class DefaultCrossRepositorySearch } } + private void parseAndAdd( BooleanQuery query, String key, String value, String delimiter ) + { + if ( value != null && value.length() > 0 ) + { + String[] terms = value.split( delimiter ); + for ( int i = 0; i < terms.length; i++ ) + { + Term valueTerm = new Term( key, terms[i] ); + Query valueQuery = new TermQuery( valueTerm ); + query.add( valueQuery, BooleanClause.Occur.MUST ); + } + } + else + { + Term valueTerm = new Term( key, value ); + Query valueQuery = new TermQuery( valueTerm ); + query.add( valueQuery, BooleanClause.Occur.MUST ); + } + } + public void initialize() throws InitializationException { diff --git a/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java b/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java index 71c872d67..005e3e719 100644 --- a/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java +++ b/archiva-modules/archiva-base/archiva-indexer/src/test/java/org/apache/maven/archiva/indexer/search/DefaultCrossRepositorySearchTest.java @@ -242,6 +242,51 @@ public class DefaultCrossRepositorySearchTest "org.apache.maven.continuum.web.action.BuildDefinitionAction.isBuildFresh", null, true ); } + public void testExecuteFilteredSearch() + throws Exception + { + CrossRepositorySearch search = lookupCrossRepositorySearch(); + + String expectedRepos[] = new String[] { TEST_DEFAULT_REPO_ID }; + + String expectedResults[] = new String[] { "org1", "org2", "org3", "org4", "org5", "org6", "org7", "org8" }; + + String secondExpectedResults[] = new String[] { "continuum-webapp" }; + + String thirdExpectedResults[] = new String[] { "archiva-common" }; + + // search for groupId + assertFilteredSearchResults( expectedRepos, expectedResults, search, "org", null, null, null, 30 ); + + // search for groupId and artifactId + assertFilteredSearchResults( expectedRepos, secondExpectedResults, search, "org.apache.maven", + "continuum-webapp", null, null, 30 ); + + // search for groupId , artifactId and version + assertFilteredSearchResults( expectedRepos, thirdExpectedResults, search, "org.apache.maven.archiva", + "archiva-common", "1.0", null, 30 ); + } + + private void assertFilteredSearchResults ( String expectedRepos[], String expectedResults[], CrossRepositorySearch search, + String groupId, String artifactId, String version, String className , int rowCount ) + { + SearchResultLimits limits = new SearchResultLimits( 0 ); + limits.setPageSize( rowCount ); + + List<String> selectedRepos = new ArrayList<String>(); + selectedRepos.addAll( Arrays.asList( expectedRepos ) ); + + SearchResults results = null; + + results = search.executeFilteredSearch( "guest" , selectedRepos, groupId, artifactId, version, className, limits ); + + assertNotNull( "Search Results should not be null.", results ); + assertEquals( "Repository Hits", expectedRepos.length, results.getRepositories().size() ); + assertEquals( expectedRepos.length, 1); + assertEquals( TEST_DEFAULT_REPO_ID , selectedRepos.get( 0 ) ); + assertEquals( "Search Result Hits", expectedResults.length, results.getHits().size() ); + } + private void assertSearchResults( String expectedRepos[], String expectedResults[], CrossRepositorySearch search, String term, List<String> previousSearchTerms, boolean bytecode ) throws Exception @@ -251,11 +296,12 @@ public class DefaultCrossRepositorySearchTest List<String> selectedRepos = new ArrayList<String>(); selectedRepos.addAll( Arrays.asList( expectedRepos ) ); - + SearchResults results = null; + if( previousSearchTerms == null ) - { - if( bytecode ) + { + if( bytecode ) { results = search.searchForBytecode( "guest", selectedRepos, term, limits ); } @@ -268,16 +314,18 @@ public class DefaultCrossRepositorySearchTest { results = search.searchForTerm( "guest", selectedRepos, term, limits, previousSearchTerms ); } + assertNotNull( "Search Results should not be null.", results ); assertEquals( "Repository Hits", expectedRepos.length, results.getRepositories().size() ); + // TODO: test the repository ids returned. assertEquals( "Search Result Hits", expectedResults.length, results.getHits().size() ); // TODO: test the order of hits. // TODO: test the value of the hits. } - + protected ManagedRepositoryConfiguration createRepository( String id, String name, File location ) { ManagedRepositoryConfiguration repo = new ManagedRepositoryConfiguration(); diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java index 795d3712c..35e770b26 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/SearchAction.java @@ -1,5 +1,5 @@ package org.apache.maven.archiva.web.action; - + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -23,10 +23,12 @@ import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; -import com.opensymphony.xwork.ActionContext; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; +import org.apache.maven.archiva.configuration.ArchivaConfiguration; +import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration; import org.apache.maven.archiva.database.ArchivaDAO; import org.apache.maven.archiva.database.Constraint; import org.apache.maven.archiva.database.constraints.ArtifactsByChecksumConstraint; @@ -42,6 +44,9 @@ import org.apache.maven.archiva.security.PrincipalNotFoundException; import org.apache.maven.archiva.security.UserRepositories; import org.codehaus.plexus.xwork.action.PlexusActionSupport; +import com.opensymphony.xwork.ActionContext; +import com.opensymphony.xwork.Preparable; + /** * Search all indexed fields by the given criteria. * @@ -49,10 +54,16 @@ import org.codehaus.plexus.xwork.action.PlexusActionSupport; */ public class SearchAction extends PlexusActionSupport -{ + implements Preparable +{ /** * Query string. */ + + private ArchivaConfiguration archivaConfiguration; + + private Map<String, ManagedRepositoryConfiguration> managedRepositories; + private String q; /** @@ -90,27 +101,132 @@ public class SearchAction private int totalPages; - private boolean searchResultsOnly; + private boolean searchResultsOnly; private String completeQueryString; private static final String COMPLETE_QUERY_STRING_SEPARATOR = ";"; - + private static final String BYTECODE_KEYWORD = "bytecode:"; + private List<String> managedRepositoryList; + + private String groupId; + + private String artifactId; + + private String version; + + private String className; + + private int rowCount = 30; + + private String repositoryId; + + private boolean fromFilterSearch; + + private boolean filterSearch = false; + + private boolean fromResultsPage; + + private int num; + + public boolean isFromResultsPage() + { + return fromResultsPage; + } + + public void setFromResultsPage( boolean fromResultsPage ) + { + this.fromResultsPage = fromResultsPage; + } + + public boolean isFromFilterSearch() + { + return fromFilterSearch; + } + + public void setFromFilterSearch( boolean fromFilterSearch ) + { + this.fromFilterSearch = fromFilterSearch; + } + + public void prepare() + { + managedRepositoryList = new ArrayList<String>(); + managedRepositoryList = getObservableRepos(); + + if ( managedRepositoryList.size() > 0 ) + { + managedRepositoryList.add( "all" ); + } + } + + // advanced search MRM-90 -- filtered search + public String filteredSearch() + throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException + { + fromFilterSearch = true; + + if ( CollectionUtils.isEmpty( managedRepositoryList ) ) + { + return GlobalResults.ACCESS_TO_NO_REPOS; + } + + SearchResultLimits limits = new SearchResultLimits( currentPage ); + + limits.setPageSize( rowCount ); + List<String> selectedRepos = new ArrayList<String>(); + + if ( repositoryId.equals( "all" ) ) + { + selectedRepos = getObservableRepos(); + } + else + { + selectedRepos.add( repositoryId ); + } + + if ( CollectionUtils.isEmpty( selectedRepos ) ) + { + return GlobalResults.ACCESS_TO_NO_REPOS; + } + + results = + crossRepoSearch.executeFilteredSearch( getPrincipal(), selectedRepos, groupId, artifactId, version, + className, limits ); + + if ( results.isEmpty() ) + { + addActionError( "No results found" ); + return INPUT; + } + + totalPages = results.getTotalHits() / limits.getPageSize(); + + if ( ( results.getTotalHits() % limits.getPageSize() ) != 0 ) + { + totalPages = totalPages + 1; + } + + return SUCCESS; + } + public String quickSearch() throws MalformedURLException, RepositoryIndexException, RepositoryIndexSearchException { /* TODO: give action message if indexing is in progress. * This should be based off a count of 'unprocessed' artifacts. - * This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet + * This (yet to be written) routine could tell the user that X (unprocessed) artifacts are not yet * present in the full text search. */ assert q != null && q.length() != 0; - + + fromFilterSearch = false; + SearchResultLimits limits = new SearchResultLimits( currentPage ); - + List<String> selectedRepos = getObservableRepos(); if ( CollectionUtils.isEmpty( selectedRepos ) ) { @@ -118,13 +234,13 @@ public class SearchAction } if( isBytecodeSearch( q ) ) - { + { results = crossRepoSearch.searchForBytecode( getPrincipal(), selectedRepos, removeKeyword( q ), limits ); } else { if( searchResultsOnly && !completeQueryString.equals( "" ) ) - { + { results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits, parseCompleteQueryString() ); } else @@ -133,33 +249,33 @@ public class SearchAction results = crossRepoSearch.searchForTerm( getPrincipal(), selectedRepos, q, limits ); } } - + if ( results.isEmpty() ) { addActionError( "No results found" ); return INPUT; } - + totalPages = results.getTotalHits() / limits.getPageSize(); - + if( (results.getTotalHits() % limits.getPageSize()) != 0 ) { totalPages = totalPages + 1; } // TODO: filter / combine the artifacts by version? (is that even possible with non-artifact hits?) - /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in + /* I don't think that we should, as I expect us to utilize the 'score' system in lucene in * the future to return relevant links better. * I expect the lucene scoring system to take multiple hits on different areas of a single document - * to result in a higher score. + * to result in a higher score. * - Joakim */ - + if( !isEqualToPreviousSearchTerm( q ) ) { buildCompleteQueryString( q ); } - + return SUCCESS; } @@ -185,10 +301,10 @@ public class SearchAction if ( databaseResults.size() == 1 ) { - // 1 hit? return it's information directly! + // 1 hit? return it's information directly! return ARTIFACT; } - + return RESULTS; } @@ -197,12 +313,12 @@ public class SearchAction { return INPUT; } - + private String getPrincipal() { return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() ); } - + private List<String> getObservableRepos() { try @@ -227,44 +343,44 @@ public class SearchAction private void buildCompleteQueryString( String searchTerm ) { - if( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 ) + if ( searchTerm.indexOf( COMPLETE_QUERY_STRING_SEPARATOR ) != -1 ) { searchTerm = StringUtils.remove( searchTerm, COMPLETE_QUERY_STRING_SEPARATOR ); } - - if( completeQueryString == null || "".equals( completeQueryString ) ) + + if ( completeQueryString == null || "".equals( completeQueryString ) ) { completeQueryString = searchTerm; } else - { + { completeQueryString = completeQueryString + COMPLETE_QUERY_STRING_SEPARATOR + searchTerm; } } - + private List<String> parseCompleteQueryString() { - List<String> parsedCompleteQueryString = new ArrayList<String>(); + List<String> parsedCompleteQueryString = new ArrayList<String>(); String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR ); CollectionUtils.addAll( parsedCompleteQueryString, parsed ); - + return parsedCompleteQueryString; } - + private boolean isEqualToPreviousSearchTerm( String searchTerm ) { - if( !"".equals( completeQueryString ) ) + if ( !"".equals( completeQueryString ) ) { String[] parsed = StringUtils.split( completeQueryString, COMPLETE_QUERY_STRING_SEPARATOR ); - if( StringUtils.equalsIgnoreCase( searchTerm, parsed[ parsed.length - 1 ] ) ) + if ( StringUtils.equalsIgnoreCase( searchTerm, parsed[parsed.length - 1] ) ) { return true; } } - + return false; } - + public String getQ() { return q; @@ -284,12 +400,12 @@ public class SearchAction { return databaseResults; } - + public void setCurrentPage( int page ) { this.currentPage = page; } - + public int getCurrentPage() { return currentPage; @@ -323,23 +439,123 @@ public class SearchAction public void setCompleteQueryString( String completeQueryString ) { this.completeQueryString = completeQueryString; - } - + } + private boolean isBytecodeSearch( String queryString ) { - if( queryString.startsWith( BYTECODE_KEYWORD ) ) + if ( queryString.startsWith( BYTECODE_KEYWORD ) ) { - return true; + return true; } - + return false; } - + private String removeKeyword( String queryString ) - { + { String qString = StringUtils.uncapitalize( queryString ); - qString= StringUtils.remove( queryString, BYTECODE_KEYWORD ); - + qString = StringUtils.remove( queryString, BYTECODE_KEYWORD ); + return qString; } + + public ArchivaConfiguration getArchivaConfiguration() + { + return archivaConfiguration; + } + + public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration ) + { + this.archivaConfiguration = archivaConfiguration; + } + + public Map<String, ManagedRepositoryConfiguration> getManagedRepositories() + { + return getArchivaConfiguration().getConfiguration().getManagedRepositoriesAsMap(); + } + + public void setManagedRepositories( Map<String, ManagedRepositoryConfiguration> managedRepositories ) + { + this.managedRepositories = managedRepositories; + } + + public String getGroupId() + { + return groupId; + } + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + public String getArtifactId() + { + return artifactId; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + + public String getVersion() + { + return version; + } + + public void setVersion( String version ) + { + this.version = version; + } + + public int getRowCount() + { + return rowCount; + } + + public void setRowCount( int rowCount ) + { + this.rowCount = rowCount; + } + + public boolean isFilterSearch() + { + return filterSearch; + } + + public void setFilterSearch( boolean filterSearch ) + { + this.filterSearch = filterSearch; + } + + public String getRepositoryId() + { + return repositoryId; + } + + public void setRepositoryId( String repositoryId ) + { + this.repositoryId = repositoryId; + } + + public List<String> getManagedRepositoryList() + { + return managedRepositoryList; + } + + public void setManagedRepositoryList( List<String> managedRepositoryList ) + { + this.managedRepositoryList = managedRepositoryList; + } + + public String getClassName() + { + return className; + } + + public void setClassName( String className ) + { + this.className = className; + } } diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/SearchAction-filteredSearch-validation.xml b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/SearchAction-filteredSearch-validation.xml new file mode 100644 index 000000000..5f9c89750 --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/org/apache/maven/archiva/web/action/SearchAction-filteredSearch-validation.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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. + --> + +<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" + "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> + +<validators> + <field name="rowCount"> + <field-validator type="int"> + <message>Invalid entry</message> + </field-validator> + <field-validator type="int"> + <param name="min">1</param> + <message>Row count must be larger than ${min}.</message> + </field-validator> + </field> +</validators> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml index 88297d58a..36509e11e 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/xwork.xml @@ -143,6 +143,16 @@ <action name="index" class="searchAction" method="input"> <result name="input">/WEB-INF/jsp/quickSearch.jsp</result> </action> + + <action name="advancedSearch" class="searchAction" method="input"> + <result name="input">/WEB-INF/jsp/advancedSearch.jsp</result> + </action> + + <action name="filteredSearch" class="searchAction" method="filteredSearch"> + <result name="input">/WEB-INF/jsp/advancedSearch.jsp</result> + <result>/WEB-INF/jsp/results.jsp</result> + <result name="error">/WEB-INF/jsp/quickSearch.jsp</result> + </action> <action name="quickSearch" class="searchAction" method="quickSearch"> <result name="input">/WEB-INF/jsp/quickSearch.jsp</result> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/advancedSearch.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/advancedSearch.jsp new file mode 100644 index 000000000..3a87d1f5e --- /dev/null +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/advancedSearch.jsp @@ -0,0 +1,67 @@ +<%-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ 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 prefix="ww" uri="/webwork" %> +<html> +<head> + <title>Advanced Search</title> + <ww:head/> +</head> + +<ww:if test="%{infoMessage != null}"> + <p>${infoMessage}</p> +</ww:if> + +<body> + +<h1>Advanced Search</h1> + + +<div id="contentArea"> + + <div id="searchBox"> + + <ww:form method="get" action="filteredSearch" validate="true"> + <ww:textfield label="Row Count" size="50" name="rowCount"/> + <ww:textfield label="Group Id" size="50" name="groupId"/> + <ww:textfield label="Artifact Id" size="50" name="artifactId"/> + <ww:textfield label="Version" size="50" name="version"/> + <ww:textfield label="Class / Package" size="50" name="className"/> + <ww:select name="repositoryId" label="Repository ID" list="managedRepositoryList"/> + <ww:hidden name="completeQueryString" value="${completeQueryString}"/> + <ww:hidden name="fromFilterSearch" value="${fromFilterSearch}"/> + <ww:submit label="Go!"/> + </ww:form> + + <ww:url id="indexUrl" action="index"/> + <ww:a href="%{indexUrl}"> + Quick Search Page + </ww:a> + + </div> + + <script type="text/javascript"> + document.getElementById("filteredSearch_groupId").focus(); + </script> + <ww:actionerror/> + +</div> + +</body> +</html> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp index 537bc6c84..45a435e75 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/quickSearch.jsp @@ -44,6 +44,11 @@ document.getElementById("quickSearch_q").focus(); </script> + <ww:url id="filteredSearchUrl" action="advancedSearch"/> + <ww:a href="%{filteredSearchUrl}"> + Advanced Search + </ww:a> + <p> <ww:actionerror/> </p> diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp index 889fa75dd..910494540 100644 --- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp +++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/results.jsp @@ -30,7 +30,12 @@ <body> -<h1>Search</h1> +<c:if test="${fromFilterSearch == true}"> + <h1>Advanced Search</h1> +</c:if> +<c:if test="${fromFilterSearch == false}"> + <h1>Search</h1> +</c:if> <c:url var="imgNextPageUrl" value="/images/icon_next_page.gif"/> <c:url var="imgPrevPageUrl" value="/images/icon_prev_page.gif"/> @@ -39,20 +44,44 @@ <div id="contentArea"> <div id="searchBox"> - <ww:form method="get" action="quickSearch" validate="true"> - <ww:textfield label="Search for" size="50" name="q"/> - <ww:checkbox label="Search within results" name="searchResultsOnly"/> - <ww:hidden name="completeQueryString" value="${completeQueryString}"/> - <ww:submit label="Go!"/> - </ww:form> - <script type="text/javascript"> - document.getElementById("quickSearch_q").focus(); - </script> + <c:if test="${fromFilterSearch == true}"> + <ww:form method="get" action="filteredSearch" validate="true"> + <ww:textfield label="Row Count" size="50" name="rowCount"/> + <ww:textfield label="Group Id" size="50" name="groupId"/> + <ww:textfield label="Artifact Id" size="50" name="artifactId"/> + <ww:textfield label="Version" size="50" name="version"/> + <ww:textfield label="Class / Package" size="50" name="className"/> + <ww:select name="repositoryId" label="Repository ID" list="managedRepositoryList"/> + <ww:hidden name="completeQueryString" value="${completeQueryString}"/> + <ww:hidden name="fromFilterSearch" value="${fromFilterSearch}"/> + <ww:submit label="Go!"/> + </ww:form> + + <ww:url id="indexUrl" action="index"/> + <ww:a href="%{indexUrl}"> + Quick Search Page + </ww:a> + <script type="text/javascript"> + document.getElementById("filteredSearch_groupId").focus(); + </script> + </c:if> + <c:if test="${fromFilterSearch == false}"> + <ww:form method="get" action="quickSearch" validate="true"> + <ww:textfield label="Search for" size="50" name="q"/> + <ww:checkbox label="Search within results" name="searchResultsOnly"/> + <ww:hidden name="completeQueryString" value="${completeQueryString}"/> + <ww:submit label="Go!"/> + </ww:form> + <script type="text/javascript"> + document.getElementById("quickSearch_q").focus(); + </script> + </c:if> <p> <ww:actionerror/> </p> + </div> <h1>Results</h1> @@ -62,10 +91,10 @@ <%-- search was made from the indices --%> <c:when test="${databaseResults == null}"> - <c:set var="hitsNum">${fn:length(results.hits) + (currentPage * results.limits.pageSize)}</c:set> + <c:set var="hitsNum">${fn:length(results.hits) + (currentPage * results.limits.pageSize)}</c:set> <c:choose> <c:when test="${results.totalHits > results.limits.pageSize}"> - <p>Hits: ${(hitsNum - results.limits.pageSize) + 1} to ${hitsNum} of ${results.totalHits}</p> + <p>Hits: ${(hitsNum - results.limits.pageSize) + 1} to ${hitsNum} of ${results.totalHits}</p> </c:when> <c:otherwise> <p>Hits: 1 to ${hitsNum} of ${results.totalHits}</p> @@ -80,22 +109,55 @@ <%-- Pagination start --%> <p> <%-- Prev & Next icons --%> - <c:set var="prevPageUrl"> - <ww:url action="quickSearch" namespace="/"> - <ww:param name="q" value="%{'${q}'}"/> - <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> - <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> - <ww:param name="currentPage" value="%{'${currentPage - 1}'}"/> - </ww:url> - </c:set> - <c:set var="nextPageUrl"> - <ww:url action="quickSearch" namespace="/"> - <ww:param name="q" value="%{'${q}'}"/> - <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> - <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> - <ww:param name="currentPage" value="%{'${currentPage + 1}'}"/> - </ww:url> - </c:set> + <c:if test="${fromFilterSearch == false}"> + <c:set var="prevPageUrl"> + <ww:url action="quickSearch" namespace="/"> + <ww:param name="q" value="%{'${q}'}"/> + <ww:param name="currentPage" value="%{'${currentPage - 1}'}"/> + </ww:url> + </c:set> + <c:set var="nextPageUrl"> + <ww:url action="quickSearch" namespace="/"> + <ww:param name="q" value="%{'${q}'}"/> + <ww:param name="currentPage" value="%{'${currentPage + 1}'}"/> + </ww:url> + </c:set> + </c:if> + + <c:if test="${fromFilterSearch == true}"> + <c:set var="prevPageUrl"> + <ww:url action="filteredSearch" namespace="/"> + <%-- <ww:param name="q" value="%{'${q}'}"/> --%> + <ww:param name="rowCount" value="%{'${rowCount}'}"/> + <ww:param name="groupId" value="%{'${groupId}'}"/> + <ww:param name="artifactId" value="%{'${artifactId}'}"/> + <ww:param name="version" value="%{'${version}'}"/> + <ww:param name="className" value="%{'${className}'}"/> + <ww:param name="repositoryId" value="%{'${repositoryId}'}"/> + <ww:param name="filterSearch" value="%{'${filterSearch}'}"/> + <ww:param name="fromResultsPage" value="true"/> + <ww:param name="currentPage" value="%{'${currentPage - 1}'}"/> + <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> + <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> + </ww:url> + </c:set> + <c:set var="nextPageUrl"> + <ww:url action="filteredSearch" namespace="/"> +<%-- <ww:param name="q" value="%{'${q}'}"/> --%> + <ww:param name="rowCount" value="%{'${rowCount}'}"/> + <ww:param name="groupId" value="%{'${groupId}'}"/> + <ww:param name="artifactId" value="%{'${artifactId}'}"/> + <ww:param name="version" value="%{'${version}'}"/> + <ww:param name="className" value="%{'${className}'}"/> + <ww:param name="repositoryId" value="%{'${repositoryId}'}"/> + <ww:param name="filterSearch" value="%{'${filterSearch}'}"/> + <ww:param name="fromResultsPage" value="true"/> + <ww:param name="currentPage" value="%{'${currentPage + 1}'}"/> + <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> + <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> + </ww:url> + </c:set> + </c:if> <c:choose> <c:when test="${currentPage == 0}"> @@ -133,25 +195,54 @@ </c:choose> <c:forEach var="i" begin="${beginVal}" end="${endVal}"> - <c:choose> - <c:when test="${i != currentPage}"> - <c:set var="specificPageUrl"> - <ww:url action="quickSearch" namespace="/"> - <ww:param name="q" value="%{'${q}'}"/> - <ww:param name="currentPage" value="%{'${i}'}"/> - <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> - <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> - </ww:url> - </c:set> - <a href="${specificPageUrl}">${i + 1}</a> - </c:when> - <c:otherwise> - <b>${i + 1}</b> - </c:otherwise> - </c:choose> + <c:if test="${fromFilterSearch == false}"> + <c:choose> + <c:when test="${i != currentPage}"> + <c:set var="specificPageUrl"> + <ww:url action="quickSearch" namespace="/"> + <ww:param name="q" value="%{'${q}'}"/> + <ww:param name="currentPage" value="%{'${i}'}"/> + <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> + <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> + </ww:url> + </c:set> + <a href="${specificPageUrl}">${i + 1}</a> + </c:when> + <c:otherwise> + <b>${i + 1}</b> + </c:otherwise> + </c:choose> + </c:if> + + <c:if test="${fromFilterSearch == true}"> + <c:choose> + <c:when test="${i != currentPage}"> + <c:set var="specificPageUrl"> + <ww:url action="filteredSearch" namespace="/"> +<%-- <ww:param name="q" value="%{'${q}'}"/> --%> + <ww:param name="rowCount" value="%{'${rowCount}'}"/> + <ww:param name="groupId" value="%{'${groupId}'}"/> + <ww:param name="artifactId" value="%{'${artifactId}'}"/> + <ww:param name="version" value="%{'${version}'}"/> + <ww:param name="className" value="%{'${className}'}"/> + <ww:param name="repositoryId" value="%{'${repositoryId}'}"/> + <ww:param name="filterSearch" value="%{'${filterSearch}'}"/> + <ww:param name="fromResultsPage" value="true"/> + <ww:param name="currentPage" value="%{'${i}'}"/> + <ww:param name="searchResultsOnly" value="%{'${searchResultsOnly}'}"/> + <ww:param name="completeQueryString" value="%{'${completeQueryString}'}"/> + </ww:url> + </c:set> + <a href="${specificPageUrl}">${i + 1}</a> + </c:when> + <c:otherwise> + <b>${i + 1}</b> + </c:otherwise> + </c:choose> + </c:if> </c:forEach> - - <c:choose> + + <c:choose> <c:when test="${currentPage == (totalPages - 1)}"> <img src="${imgNextPageDisabledUrl}"/> </c:when> |