package org.podcastpedia.web.searching;
import org.apache.log4j.Logger;
import org.podcastpedia.common.controllers.propertyeditors.MediaTypeEditor;
import org.podcastpedia.common.domain.Category;
import org.podcastpedia.common.types.LanguageCode;
import org.podcastpedia.common.types.MediaType;
import org.podcastpedia.common.types.OrderByOption;
import org.podcastpedia.common.types.SearchModeType;
import org.podcastpedia.core.categories.CategoryService;
import org.podcastpedia.core.searching.SearchData;
import org.podcastpedia.core.searching.SearchResult;
import org.podcastpedia.core.searching.SearchService;
import org.podcastpedia.core.user.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* Annotation-driven controller that handles searching for podcasts.
*
* "advancedSearchData" is an object passed along the session to navigate from
* search bar to advanced search results and from one page result to the other.
*
* @author Ama
*
*/
@Controller
@RequestMapping("/search")
public class SearchController {
protected static Logger LOG = Logger.getLogger(SearchController.class);
@Autowired
private SearchService searchService;
@Autowired
private CategoryService categoryService;
@Autowired
private UserService userService;
/**
* If the users decides to write "http://localhost:8080/search/" into the
* browser she should get redirected to the advanced search formular
*
* @return
*/
@RequestMapping(method = RequestMethod.GET)
public String redirectToAdvancedSearch() {
return "redirect:/search/advanced_search";
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(MediaType.class, new MediaTypeEditor(
MediaType.class));
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));/* Converts empty strings into null when a form is submitted */
}
@RequestMapping(value = "/advanced_search", method = RequestMethod.GET)
public String prepareFormForAdvancedSearch(
@RequestParam(value = "noResultsFound", required = false) Boolean noResultsFound,
SearchData advancedSearchData, Model model) {
LOG.debug("------ prepareFormForAdvancedSearch : Received request to show advanced search form -----");
SearchData formSearchData = new SearchData();
formSearchData.setNumberResultsPerPage(10);
formSearchData.setSearchMode("natural");
formSearchData.setCurrentPage(1);
List<Category> categoriesThatHavePodcasts = categoryService
.getCategoriesOrderedByNoOfPodcasts();
model.addAttribute("allCategories", categoriesThatHavePodcasts);
model.addAttribute("isAdvancedSearchPage", true);
model.addAttribute("mediaTypes", MediaType.values());
model.addAttribute("languageCodes", LanguageCode.values());
model.addAttribute("orderByOptions", OrderByOption.values());
// it could be redirected here if no results are found and a proper
// message should be displayed to try again
if (null != noResultsFound && noResultsFound) {
model.addAttribute("noResultsFound", noResultsFound);
}
// set radio button checked by default for searchInNaturalMode
formSearchData.setSearchMode(SearchModeType.NATURAL_MODE.getValue());
// set default podcasts the default target to look in
formSearchData.setSearchTarget("episodes");
model.addAttribute("advancedSearchData", formSearchData);
return "m_advancedSearchForm";
}
/**
* Resolves the view from the advanced search form, from search form under the
* menu in start page, from categories or from tags AND from links to
* next/previous page and so on.
*
* @param advancedSearchData
* @param bindingResult
* @param model
* @return
* @throws UnsupportedEncodingException
*/
@RequestMapping(value = "/advanced_search/results", method = RequestMethod.GET)
public ModelAndView getAdvancedSearchResults(
@ModelAttribute("advancedSearchData") SearchData advancedSearchData,
BindingResult bindingResult, Model model,
HttpServletRequest httpRequest) throws UnsupportedEncodingException {
LOG.debug("------ getAdvancedSearchResults : get SEARCH RESULTS based on advanced search form -----");
if (advancedSearchData.getSearchTarget() == null)
advancedSearchData.setSearchTarget("episodes");
SearchResult searchResult;
boolean targetIsPodcasts = isTargetPodcasts(advancedSearchData);
if(targetIsPodcasts){
searchResult = searchService.getPodcastsForSearchCriteria(advancedSearchData);
boolean userAuthenticated = SecurityContextHolder.getContext().getAuthentication() != null &&
SecurityContextHolder.getContext().getAuthentication().isAuthenticated() &&
//when Anonymous Authentication is enabled
!(SecurityContextHolder.getContext().getAuthentication()
instanceof AnonymousAuthenticationToken);
//load in model subscription categories if user is signed in, so that she can add current podcast
//to one of the categories
if(userAuthenticated) {
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
List<String> subscriptionCategoryNames = userService.getSubscriptionCategoryNames(userDetails.getUsername());
model.addAttribute("subscriptionCategories", subscriptionCategoryNames);
}
} else {
searchResult = searchService.getResultsForSearchCriteria(advancedSearchData);
}
String redirectUrl = null;
String tilesDef = null;
ModelAndView mv;
// no results found
if (searchResult.getResults().isEmpty()) {
bindingResult.rejectValue("queryText", "notFound", "not found");
redirectUrl = "/search/advanced_search?noResultsFound=true";
} else if (searchResult.getResults().size() > 1) {
String query = httpRequest.getQueryString();
query = query.substring(0, query.lastIndexOf("¤tPage="));
if (advancedSearchData.getNrOfResults() == null) {
query += "&nrOfResults=" + searchResult.getNumberOfItemsFound()
+ "&nrResultPages=" + searchResult.getNumberOfPages();
}
model.addAttribute("queryString", query.replaceAll("&", "&"));
model.addAttribute("advancedSearchResult", searchResult);
model.addAttribute("numberOfResults",
searchResult.getNumberOfItemsFound());
model.addAttribute("numberOfPages", searchResult.getNumberOfPages());
if(targetIsPodcasts){
model.addAttribute("podcasts", searchResult.getPodcasts());
tilesDef = "search_results_podcasts_def";
} else {
model.addAttribute("episodes", searchResult.getEpisodes());
tilesDef = "search_results_def";
}
} else {
// exactly one result found (either podcast or episode), redirect to it
redirectUrl = searchResult.getResults().get(0).getRelativeLink();
}
if (tilesDef != null) {
mv = new ModelAndView();
mv.setViewName(tilesDef);
} else {
// must be a redirect
RedirectView rv = new RedirectView();
rv.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
rv.setUrl(redirectUrl);
mv = new ModelAndView(rv);
}
return mv;
}
private boolean isTargetPodcasts(SearchData searchData) {
return searchData.getSearchTarget() !=null && searchData.getSearchTarget().equals("podcasts");
}
}