diff --git a/src/main/java/ru/mystamps/web/Url.java b/src/main/java/ru/mystamps/web/Url.java index 66df10de7b..d8af9ae75a 100644 --- a/src/main/java/ru/mystamps/web/Url.java +++ b/src/main/java/ru/mystamps/web/Url.java @@ -52,6 +52,8 @@ public final class Url { public static final String ADD_IMAGE_SERIES_PAGE = "/series/{id}/image"; public static final String SEARCH_SERIES_BY_CATALOG = "/series/search/by_catalog"; + public static final String SUGGEST_SERIES_COUNTRY = "/suggest/series_country"; + public static final String ADD_CATEGORY_PAGE = "/category/add"; public static final String LIST_CATEGORIES_PAGE = "/category/list"; public static final String INFO_CATEGORY_PAGE = "/category/{slug}"; @@ -77,7 +79,7 @@ public final class Url { public static final String ADD_SERIES_WITH_COUNTRY_PAGE = "/series/add/country/{slug}"; // MUST be updated when any of our resources were modified - public static final String RESOURCES_VERSION = "v0.3.0"; + public static final String RESOURCES_VERSION = "v0.3.1"; // CheckStyle: ignore LineLength for next 4 lines public static final String MAIN_CSS = "/static/" + RESOURCES_VERSION + "/styles/main.min.css"; @@ -125,6 +127,7 @@ public static Map asMap(boolean serveContentFromSingleHost) { map.put("INFO_SERIES_PAGE", INFO_SERIES_PAGE); map.put("ADD_IMAGE_SERIES_PAGE", ADD_IMAGE_SERIES_PAGE); map.put("SEARCH_SERIES_BY_CATALOG", SEARCH_SERIES_BY_CATALOG); + map.put("SUGGEST_SERIES_COUNTRY", SUGGEST_SERIES_COUNTRY); map.put("ADD_CATEGORY_PAGE", ADD_CATEGORY_PAGE); map.put("INFO_CATEGORY_PAGE", INFO_CATEGORY_PAGE); map.put("LIST_CATEGORIES_PAGE", LIST_CATEGORIES_PAGE); diff --git a/src/main/java/ru/mystamps/web/config/ControllersConfig.java b/src/main/java/ru/mystamps/web/config/ControllersConfig.java index 382358d1e9..413ccac7fd 100644 --- a/src/main/java/ru/mystamps/web/config/ControllersConfig.java +++ b/src/main/java/ru/mystamps/web/config/ControllersConfig.java @@ -110,5 +110,12 @@ public SiteController getSiteController() { public SitemapController getSitemapController() { return new SitemapController(servicesConfig.getSeriesService()); } - + + @Bean + public SuggestionController getSuggestionController() { + return new SuggestionController( + servicesConfig.getCountryService() + ); + } + } diff --git a/src/main/java/ru/mystamps/web/controller/SuggestionController.java b/src/main/java/ru/mystamps/web/controller/SuggestionController.java new file mode 100644 index 0000000000..13e332e42b --- /dev/null +++ b/src/main/java/ru/mystamps/web/controller/SuggestionController.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009-2017 Slava Semushin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package ru.mystamps.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import lombok.RequiredArgsConstructor; + +import ru.mystamps.web.Url; +import ru.mystamps.web.controller.converter.annotation.CurrentUser; +import ru.mystamps.web.service.CountryService; + +@Controller +@RequiredArgsConstructor +public class SuggestionController { + + private final CountryService countryService; + + /** + * @author John Shkarin + */ + @ResponseBody + @GetMapping(Url.SUGGEST_SERIES_COUNTRY) + public String suggestCountryForUser(@CurrentUser Integer currentUserId) { + return countryService.suggestCountryForUser(currentUserId); + } + +} + diff --git a/src/main/java/ru/mystamps/web/dao/CountryDao.java b/src/main/java/ru/mystamps/web/dao/CountryDao.java index 6f3718ba57..6691af5ab0 100644 --- a/src/main/java/ru/mystamps/web/dao/CountryDao.java +++ b/src/main/java/ru/mystamps/web/dao/CountryDao.java @@ -23,6 +23,7 @@ import ru.mystamps.web.dao.dto.AddCountryDbDto; import ru.mystamps.web.dao.dto.LinkEntityDto; +@SuppressWarnings("PMD.TooManyMethods") public interface CountryDao { Integer add(AddCountryDbDto country); long countAll(); @@ -35,4 +36,6 @@ public interface CountryDao { List getStatisticsOf(Integer collectionId, String lang); List findAllAsLinkEntities(String lang); LinkEntityDto findOneAsLinkEntity(String slug, String lang); + String findLastCreatedByUser(Integer userId); + String findPopularCountryInCollection(Integer userId); } diff --git a/src/main/java/ru/mystamps/web/dao/impl/JdbcCountryDao.java b/src/main/java/ru/mystamps/web/dao/impl/JdbcCountryDao.java index 1ed330b38d..16978393bd 100644 --- a/src/main/java/ru/mystamps/web/dao/impl/JdbcCountryDao.java +++ b/src/main/java/ru/mystamps/web/dao/impl/JdbcCountryDao.java @@ -39,7 +39,7 @@ import ru.mystamps.web.dao.dto.LinkEntityDto; @RequiredArgsConstructor -@SuppressWarnings("PMD.AvoidDuplicateLiterals") +@SuppressWarnings({"PMD.AvoidDuplicateLiterals", "PMD.TooManyMethods"}) public class JdbcCountryDao implements CountryDao { private final NamedParameterJdbcTemplate jdbcTemplate; @@ -76,6 +76,13 @@ public class JdbcCountryDao implements CountryDao { @Value("${country.find_country_link_info_by_slug}") private String findCountryLinkEntityBySlugSql; + + @Value("${country.find_last_created_by_user}") + private String findLastCreatedByUserSql; + + @SuppressWarnings("PMD.LongVariable") + @Value("${country.find_popular_country_from_user_collection}") + private String findPopularCountryInCollectionSql; @Override public Integer add(AddCountryDbDto country) { @@ -206,5 +213,37 @@ public LinkEntityDto findOneAsLinkEntity(String slug, String lang) { return null; } } + + /** + * @author Shkarin John + */ + @Override + public String findLastCreatedByUser(Integer userId) { + try { + return jdbcTemplate.queryForObject( + findLastCreatedByUserSql, + Collections.singletonMap("created_by", userId), + String.class + ); + } catch (EmptyResultDataAccessException ignored) { + return null; + } + } + + /** + * @author Shkarin John + */ + @Override + public String findPopularCountryInCollection(Integer userId) { + try { + return jdbcTemplate.queryForObject( + findPopularCountryInCollectionSql, + Collections.singletonMap("user_id", userId), + String.class + ); + } catch (EmptyResultDataAccessException ignored) { + return null; + } + } } diff --git a/src/main/java/ru/mystamps/web/service/CountryService.java b/src/main/java/ru/mystamps/web/service/CountryService.java index 4bb4c07e0d..521c5727d7 100644 --- a/src/main/java/ru/mystamps/web/service/CountryService.java +++ b/src/main/java/ru/mystamps/web/service/CountryService.java @@ -23,6 +23,7 @@ import ru.mystamps.web.dao.dto.LinkEntityDto; import ru.mystamps.web.service.dto.AddCountryDto; +@SuppressWarnings("PMD.TooManyMethods") public interface CountryService { String add(AddCountryDto dto, Integer userId); List findAllAsLinkEntities(String lang); @@ -35,4 +36,5 @@ public interface CountryService { long countAddedSince(Date date); long countUntranslatedNamesSince(Date date); List getStatisticsOf(Integer collectionId, String lang); + String suggestCountryForUser(Integer userId); } diff --git a/src/main/java/ru/mystamps/web/service/CountryServiceImpl.java b/src/main/java/ru/mystamps/web/service/CountryServiceImpl.java index 52c784d46e..9c2743db98 100644 --- a/src/main/java/ru/mystamps/web/service/CountryServiceImpl.java +++ b/src/main/java/ru/mystamps/web/service/CountryServiceImpl.java @@ -42,6 +42,7 @@ import ru.mystamps.web.util.SlugUtils; @RequiredArgsConstructor +@SuppressWarnings("PMD.TooManyMethods") public class CountryServiceImpl implements CountryService { private static final Logger LOG = LoggerFactory.getLogger(CountryServiceImpl.class); @@ -162,5 +163,33 @@ public List getStatisticsOf(Integer collectionId, String lang) { return countryDao.getStatisticsOf(collectionId, lang); } - + + /** + * @author Shkarin John + */ + @Override + @Transactional(readOnly = true) + @PreAuthorize(HasAuthority.CREATE_SERIES) + public String suggestCountryForUser(Integer userId) { + Validate.isTrue(userId != null, "User id must be non null"); + + String slug = countryDao.findLastCreatedByUser(userId); + if (slug != null) { + LOG.debug( + "Country {} has been suggested to user #{} as a recently created", + slug, + userId + ); + return slug; + } + + slug = countryDao.findPopularCountryInCollection(userId); + LOG.debug( + "Country {} has been suggested to user #{} as popular in his collection", + slug, + userId + ); + + return slug; + } } diff --git a/src/main/java/ru/mystamps/web/support/spring/security/SecurityConfig.java b/src/main/java/ru/mystamps/web/support/spring/security/SecurityConfig.java index 66a2c433d6..c27ae48789 100644 --- a/src/main/java/ru/mystamps/web/support/spring/security/SecurityConfig.java +++ b/src/main/java/ru/mystamps/web/support/spring/security/SecurityConfig.java @@ -75,6 +75,7 @@ protected void configure(HttpSecurity http) throws Exception { .mvcMatchers(Url.ADD_COUNTRY_PAGE).hasAuthority(StringAuthority.CREATE_COUNTRY) .mvcMatchers(Url.ADD_SERIES_PAGE).hasAuthority(StringAuthority.CREATE_SERIES) .mvcMatchers(Url.SITE_EVENTS_PAGE).hasAuthority(StringAuthority.VIEW_SITE_EVENTS) + .mvcMatchers(Url.SUGGEST_SERIES_COUNTRY).hasAuthority(StringAuthority.CREATE_SERIES) .regexMatchers(HttpMethod.POST, "/series/[0-9]+") .hasAnyAuthority( StringAuthority.UPDATE_COLLECTION, diff --git a/src/main/java/ru/mystamps/web/support/togglz/Features.java b/src/main/java/ru/mystamps/web/support/togglz/Features.java index ee7e8936b1..75b881e063 100644 --- a/src/main/java/ru/mystamps/web/support/togglz/Features.java +++ b/src/main/java/ru/mystamps/web/support/togglz/Features.java @@ -74,7 +74,11 @@ public enum Features implements Feature { @Label("View site events") @EnabledByDefault - VIEW_SITE_EVENTS; + VIEW_SITE_EVENTS, + + @Label("/series/add: show link with auto-suggestions") + @EnabledByDefault + INFO_COUNTRY_SERIES; public boolean isActive() { return FeatureContext.getFeatureManager().isActive(this); diff --git a/src/main/javascript/series/add.js b/src/main/javascript/series/add.js index c0c504d022..fbf20064d1 100644 --- a/src/main/javascript/series/add.js +++ b/src/main/javascript/series/add.js @@ -2,7 +2,7 @@ // IMPORTANT: // You have to update Url.RESOURCES_VERSION each time whenever you're modified this file! // -function initPage() { +function initPage(suggestCountryUrl) { $('#country').selectize(); $('.js-catalog-numbers').on('blur', function() { @@ -21,4 +21,22 @@ function initPage() { $('.js-with-tooltip').tooltip({ 'placement': 'right' }); + + if (suggestCountryUrl != null) { + $.get(suggestCountryUrl, function handleSuggestedCountry(slug) { + if (slug == "") { + return; + } + + var country = $("#js-guess-country-link"); + country.show(); + country.click(function chooseSuggestedCountry() { + $(this).hide(); + + var select_country = $("#country").selectize(); + var selectize = select_country[0].selectize; + selectize.setValue(slug); + }); + }); + } } diff --git a/src/main/resources/ru/mystamps/i18n/Messages.properties b/src/main/resources/ru/mystamps/i18n/Messages.properties index 2bc1645681..569d5796bc 100644 --- a/src/main/resources/ru/mystamps/i18n/Messages.properties +++ b/src/main/resources/ru/mystamps/i18n/Messages.properties @@ -121,6 +121,7 @@ t_add_more_images_hint = Later you will be able to add additional images t_not_chosen = Not chosen t_create_category_hint = You can also add a new category t_create_country_hint = You can also add a new country +t_guess_country = Guess a country # series/info.html t_series_info = Info about series diff --git a/src/main/resources/ru/mystamps/i18n/Messages_ru.properties b/src/main/resources/ru/mystamps/i18n/Messages_ru.properties index 67a3ae6ec1..98dc5f14d7 100644 --- a/src/main/resources/ru/mystamps/i18n/Messages_ru.properties +++ b/src/main/resources/ru/mystamps/i18n/Messages_ru.properties @@ -121,6 +121,7 @@ t_add_more_images_hint = Вы сможете добавить дополните t_not_chosen = Не выбрана t_create_category_hint = Вы также можете добавить новую категорию t_create_country_hint = Вы также можете добавить новую страну +t_guess_country = Угадать страну # series/info.html t_series_info = Информация о серии diff --git a/src/main/resources/sql/country_dao_queries.properties b/src/main/resources/sql/country_dao_queries.properties index c80cacdf84..c02d4e8559 100644 --- a/src/main/resources/sql/country_dao_queries.properties +++ b/src/main/resources/sql/country_dao_queries.properties @@ -81,3 +81,23 @@ country.find_country_link_info_by_slug = \ FROM countries c \ WHERE c.slug = :slug \ ORDER BY CASE WHEN 'ru' = :lang THEN COALESCE(c.name_ru, c.name) ELSE c.name END + +country.find_last_created_by_user = \ + SELECT c.slug \ + FROM series s \ + JOIN countries c \ + ON c.id = s.country_id \ + WHERE s.created_by = :created_by \ + ORDER BY s.created_at DESC \ + LIMIT 1 + +country.find_popular_country_from_user_collection = \ + SELECT co.slug \ + FROM collections c \ + JOIN collections_series cs ON c.id = cs.collection_id \ + JOIN series s ON s.id = cs.series_id \ + JOIN countries co ON co.id = s.country_id \ + WHERE c.user_id = :user_id \ + GROUP BY co.id \ + ORDER BY COUNT(*) DESC \ + LIMIT 1 diff --git a/src/main/webapp/WEB-INF/views/series/add.html b/src/main/webapp/WEB-INF/views/series/add.html index 6dd6288a76..e4e0a4a6ed 100644 --- a/src/main/webapp/WEB-INF/views/series/add.html +++ b/src/main/webapp/WEB-INF/views/series/add.html @@ -15,7 +15,7 @@ - +