diff --git a/.codacy.yml b/.codacy.yml
new file mode 100644
index 000000000000..311a8f4e0029
--- /dev/null
+++ b/.codacy.yml
@@ -0,0 +1,5 @@
+---
+exclude_paths:
+ - 'src/main/webapp/**'
+ - '**.md'
+ - '**.sql'
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 73e6d9e3dd11..a1aebd23c98f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,6 @@ target
*.iml
log
*.patch
+postgres.properties
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000000..b7bf2f3ba060
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,26 @@
+# https://docs.travis-ci.com/user/languages/java/
+language: java
+jdk: openjdk17
+
+#https://dzone.com/articles/travis-ci-tutorial-java-projects
+cache:
+ directories:
+ - $HOME/.m2
+
+# https://docs.travis-ci.com/user/database-setup/#PostgreSQL
+before_script:
+ - psql -c 'create database topjava' -U postgres
+ - psql -c 'create user "user"; grant all privileges on database topjava to "user"' -U postgres
+
+# https://docs.travis-ci.com/user/customizing-the-build#Building-Specific-Branches
+branches:
+ only:
+ - master
+
+# https://stackoverflow.com/a/49852690/548473:
+services:
+ - postgresql
+
+# https://docs.travis-ci.com/user/notifications#Configuring-email-notifications
+#notifications:
+# email: false
\ No newline at end of file
diff --git a/README.md b/README.md
index 228b708b7094..6cb380d182a2 100644
--- a/README.md
+++ b/README.md
@@ -19,17 +19,21 @@ Java Enterprise Online Project
Вводное занятие (обязательно смотреть все видео)
===============
-##  1. Осваиваем Java Enterprise. Трудоустройство. Ответы на вопросы.
+##  1. Осваиваем Java Enterprise. Трудоустройство. Ответы на вопросы.
- Слайды презентации
-- Java Tools and Technologies Landscape Report 2016
-- [Java in 2017 Survey](http://www.baeldung.com/java-in-2017)
-- Из юниоров в разработчики: получаем первую работу
+- [Как стать профессиональным Java разработчиком](https://www.youtube.com/watch?v=ft0Nj8Cm9kk)
+- [Популярность Java-технологий в 2019 году](https://topjava.ru/blog/sostoyanie-java-v-2019-godu)
+- [Java Technology Report 2021](https://www.jrebel.com/blog/2021-java-technology-report)
+- [The State of Developer Ecosystem 2020](https://www.jetbrains.com/lp/devecosystem-2020/java/)
+- [JVM Ecosystem Report 2021](https://snyk.io/jvm-ecosystem-report-2021/)
+- [Быть программистом: от детства к зрелости](https://www.youtube.com/watch?v=D5Hej0TyLaU)
+- [Литература](https://javaops.ru/view/books)
#### Spring Pet-Clinic
- Spring PetClinic Sample Application
- Presentation
-##  2. Системы управления версиями. Git.
+##  2. Системы управления версиями. Git.
- **Wiki по ведению проекта в Git**
- Система управления версиями. VCS/DVSC.
- Ресурсы:
@@ -41,9 +45,16 @@ Java Enterprise Online Project
- Git Overview
- [Основы Git за 20 минут](https://www.youtube.com/watch?v=TMeZGvtQnT8)
- [Git - для новичков](https://www.youtube.com/watch?list=PLY4rE9dstrJyTdVJpv7FibSaXB4BHPInb&v=PEKN8NtBDQ0)
+ - [Руководство по написанию комментариев в коммитах](https://techrocks.ru/2019/12/02/writing-good-commit-messages)
+
+##  3. Работа с проектом (выполнять инструкции)
+- **ВНИМАНИЕ: выбирайте для проекта простой пусть без пробелов и русских букв, например (Windows) `c:\projects\topjava\`. Иначе впоследствии будут проблемы**
+- **Плагин уже Git Intergation не требуется и вкладку `Version control` в IDEA переименовали в `Git`**
+
+Для переключения режима отображения изменений из вкладки Commit в Git: Local Changes нужно переключить `Settings/Preferences | Version Control | Commit | Use non-modal commit interface` или в контекстном меню вкладки `Commit`:
+
+ 
-##  3. Работа с проектом (выполнять инструкции)
-**ВНИМАНИЕ: выбирайте для проекта простой пусть без пробелов и русских букв, например (Windows) `c:\projects\topjava\`. Иначе впоследствии будут проблемы**
### Патч [prepare_to_HW0.patch](https://drive.google.com/file/d/1LNPpu9OkuCpfpD8ZJHO-o0vwu49p2i5M) (скачать и положить в каталог вашего проекта)
> Проект постоянно улучшается, поэтому видео иногда отличается от кода проекта. Изменения указываю после видео:
@@ -51,7 +62,7 @@ Java Enterprise Online Project
> - в `UserMeals/UserMealWithExcess` поля изменились на `private`
> - обновил данные `UserMealsUtil.meals` и переименовал некоторые пременные, поля и методы
> - добавил `UserMealWithExcess.toString()` и метод для выполнения _Optional домашнего задания_
-
+> - метод фильтрации в `TimeUtil` переименовали в `isBetweenHalfOpen` (также изменилась логика сравнения - `startTime` включается в интервал)
## Инструкция по шагам (из видео):
- Установить ПО (git, JDK8, IntelliJ IDEA, Maven)
@@ -69,8 +80,11 @@ Java Enterprise Online Project
- Выполнить задание и залить на GitHub (commit + push)
- Переключиться в основную ветку проекта master.
+##  4. [Тех.задание: библия или допускаются изменения. Полуоткрытый интервал.](https://drive.google.com/file/d/1BpTzjNFjS0TSekCyt_xvt6YoLvuw5KTZ/view?usp=sharing)
+- [Типы промежутков](https://ru.wikipedia.org/wiki/Промежуток_(математика))
+
##  Домашнее задание HW0
-```
+
Реализовать метод `UserMealsUtil.filteredByCycles` через циклы (`forEach`):
- должны возвращаться только записи между `startTime` и `endTime`
- поле `UserMealWithExcess.excess` должно показывать,
@@ -78,13 +92,14 @@ Java Enterprise Online Project
Т.е `UserMealWithExcess` - это запись одной еды, но поле `excess` будет одинаково для всех записей за этот день.
-- Проверьте результат выполнения ДЗ (можно проверить логику в http://topjava.herokuapp.com , список еды)
-- Оцените Time complexity алгоритма. Если она больше O(N), например O(N*N) или N*log(N), сделайте O(N).
-```
+> - Проверьте результат выполнения ДЗ (можно проверить логику в http://topjava.herokuapp.com , список еды)
+> - Оцените Time complexity алгоритма. Если она больше O(N), например O(N*N) или N*log(N), сделайте O(N).
+> **Внимание: внимательно прочитайте про O(N). O - это любой коэффициент, 2*N это тоже O(N).**
+
- Java 8 Date and Time API
-- Алгоритмы и структуры данных для начинающих: сложность алгоритмов
+- Алгоритмы и структуры данных для начинающих: сложность алгоритмов
- [Головач: сложность алгоритмов в теме коллекций](https://www.youtube.com/watch?v=Ek9ijOiplNE&feature=youtu.be&t=778)
-- Time complexity
+- Time complexity
- Временная сложность алгоритма
- Вычислительная сложность
@@ -98,27 +113,32 @@ Java Enterprise Online Project
- Java 8: Lambda выражения
- Java 8: Потоки
- Pуководство по Java 8 Stream
-- Java 8 Stream API в картинках и примерах
+- [Полное руководство по Java 8 Stream API в картинках и примерах](https://annimon.com/article/2778)
- [7 способов использовать groupingBy в Stream API](https://habrahabr.ru/post/348536)
- Лямбда-выражения в Java 8
- A Guide to Java 8
- Шпаргалка Java Stream API
- Алексея Владыкин: Элементы функционального программирования в Java
- Yakov Fain о новом в Java 8
-- stream.map vs forEach
+- stream.map vs forEach`
- - без циклов по другим коллекциям
- - решение должно быть рабочим в общем случае (не только при запуске main)
-- через Stream API за 1 проход по исходному списку `meals.streem()`
- - нельзя использовать внешние коллекции, не являющиеся частью коллектора или 2 раза проходить по исходному списку (его копиям).
- Т.е. в решении не должно быть 2 раза `meal.stream()` (в том числе неявно, в составных коллекторах)
- - возможно дополнительные проходы по частям списка
+ - без циклов по другим коллекциям/массивам (к ним также относим методы коллекций `addAll()/removeAll()`)
+ - решение должно быть рабочим в общем случае (работать в приложении с многими пользователями, не только при запуске main)
+- через Stream API за 1 проход по исходному списку `meals.stream()`
+ - нельзя использовать внешние коллекции, не являющиеся частью коллектора
+ - нельзя 2 раза проходить по исходному списку (в том числе его отфильтрованной или преобразованной копии)
+ - возможно дополнительные проходы по частям списка, при этом превышение должно считаться один раз для всего подсписка. Те например нельзя разбить список на на 2 подсписка с четными и нечетными датами и затем их объединить, с подсчетом превышения для каждого элемента.
+
+
+Ресурсы:
+- [Java 8 Stream API, часть шестая: собственный коллектор](https://easyjava.ru/java/language/java-8-stream-api-chast-shestaya-sobstvennyj-kollektor)
+- [Руководство по Java 8 Stream API: Collector](https://annimon.com/article/2778#collector)
### Замечания по использованию Stream API:
- Когда встречаешь что-то непривычное, приходится перестраивать мозги. Например, переход с процедурного на ООП программирование дается непросто. Те, кто не знает шаблонов (и не хотят учить) также их встречают плохо. Хорошая новость в том, что если это принять и начать использовать, то начинаешь получать от этого удовольствие. И тут главное не впасть в другую крайность:
@@ -133,7 +153,7 @@ Java Enterprise Online Project
##  Замечания к HW0
- 1: Код проекта менять можно! Одна из распространенных ошибок как в тестовых заданиях на собеседовании, так и при работе на проекте, что ничего нельзя менять. Конечно при правках в рабочем проекте обязательно нужно проконсультироваться/проревьюироваться у авторов кода (находится по истории VCS)
-- 2: Наследовать `UserMealWithExcess` от `UserMeal` я не буду, т.к. это разные сущности: Transfer Object и Entity. Мы будет их проходить на 2м уроке.
+- 2: Наследовать `UserMealWithExcess` от `UserMeal` нельзя, т.к. это разные сущности: Transfer Object и Entity. Мы будет их проходить на 2м уроке. Это относится и к зависимости.
- 3: Правильная реализация должна быть простой и красивой, можно сделать 2-мя способами: через стримы и через циклы. Сложность должна быть O(N), т.е. без вложенных стримов и циклов.
- 4: При реализации через циклы посмотрите в `Map` на методы `getOrDefault` или `merge`
- 5: **При реализации через `Stream` заменяйте `forEach` оператором `stream.map(..)`**
@@ -144,17 +164,14 @@ Java Enterprise Online Project
- 10: `System.out.println` нельзя делать нигде, кроме как в `main`. Позже введем логирование.
- 11: Результаты, возвращаемые `UserMealsUtil.filteredByStreams` мы будем использовать [в нашем приложении](http://topjava.herokuapp.com/) для фильтрации по времени и отображения еды правильным цветом.
- 12: Обращайте внимание на комментарии к вашим коммитам в git. Они должны быть короткие и информативные (лучше на english)
-- 13: Не полагайтесь в решении на то, что список будет подаваться отсортированным. Такого условия нет.
+- 13: Не полагайтесь в решении на то, что список еды будет подаваться отсортированным. Такого условия нет.
-----
## [Пример 7-го занятия онлайн стажировки, несколько видео открыто](https://github.com/JavaOPs/topjava/blob/master/doc/lesson07.md)
-### Полезные ресурсы
-> ВНИМАНИЕ:
-> - **ДЗ первого урока будет связано с [созданием небольшого CRUD приложения (в памяти, без DB) на JSP и сервлетах](http://danielniko.com/2012/04/17/simple-crud-using-jsp-servlet-and-mysql/)**. Введение будет, но предварительное знакомство не помешает.
-> - основы JavaSсript необходимы для понимания проекта, начиная с 8-го занятия!
-
-Все остальное - опционально.
+> - ДЗ первого урока будет связано с созданием небольшого [CRUD](https://ru.wikipedia.org/wiki/CRUD) приложения (в памяти, без базы данных) на JSP и сервлетах
+> - основы JavaSсript необходимы для понимания проекта, начиная с 8-го занятия
+### Полезные ресурсы
#### HTML, JavaScript, CSS
- [Basic HTML and HTML5](https://learn.freecodecamp.org/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements/)
- [Справочник по WEB](https://developer.mozilla.org/ru/)
@@ -172,17 +189,18 @@ Java Enterprise Online Project
#### Java (базовые вещи)
- Интуит. Программирование на Java
- 1й урок MasterJava: Многопоточность
-- Основы Java garbage collection
+- [Основы Java garbage collection](http://web.archive.org/web/20180831013112/https://ggenikus.github.io/blog/2014/05/04/gc)
- Размер Java объектов
- Введение в Java Reflection API
- Структуры данных в картинках
- Обзор java.util.concurrent.*
-- Синхронизация потоков
+- Синхронизация потоков
- String literal pool
- Маленькие хитрости Java
- A Guide to Java 8
### Туториалы, разное
+- [Открытый курс: Spring Boot + HATEOAS](https://javaops.ru/view/bootjava)
- [Что нужно знать о бэкенде новичку в веб-разработке](https://tproger.ru/translations/backend-web-development)
- [Туториалы: Spring Framework, Hibernate, Java Core, JDBC](http://proselyte.net/tutorials/)
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index f4e7412eb816..68ebc2ef69d7 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -1,8 +1,62 @@
# TopJava Release Notes
+
+### Topjava 23
+- migrate to JDK 16
+- в новой spring-data-jpa `getOne` заменили на `getById`
+- в UserUtil#prepareToSave убрал проверку пароля на `hasText`. На UI поле проверяется на `@NotBlank`
+- `ProfileRestController#register` делаю по правилам REST (POST без "/register")
+- css стили `data-...` сделал [low-case через дефисы](https://stackoverflow.com/questions/36176474/548473)
+- `TestMatcher` переименовал в `MatcherFactory`
+- Для Swagger UI пометил `AuthorizedUser` аннотацией `@ApiIgnore`
+
+### Topjava 22
+ - очистка пароля `AuthorizedUser#userTo`
+ - заменил `@SafeHtml`, который удалили из `hibernate.validator` на [Jsoup.clean](https://stackoverflow.com/a/68888601/548473)
+ - перенес запрет на обновление admin/user в `UserService`
+ - проверку email на уникальность для update с `id=null` в теле запроса сделал на основе анализа `HttpServletRequest.getRequestURI()`
+ - проверку класса в `classpath` в `Profiles#getActiveDbProfile` делаю на `org.springframework.util.ClassUtils#isPresent`
+ - удалил `type="text/javascript"`
+
+### Topjava 21
+- **добавили документирование REST API: Swagger**
+- мигрировали на JDK 15 и используем текстовые блоки
+- Вынес `produces = MediaType.APPLICATION_JSON_VALUE` на уровень контроллеров
+- Правильно используем [глабальные переменные в js](https://stackoverflow.com/a/5064235/548473)
+- Зарефакторил `inputField.tag`
+- Тестовые переменные переименовал из UPPERCASE в camelCase
+- Из тестов сервисов убрал `throws Exception` (в IDEA больше не генерятся по умолчанию)
+- **Мигрировали на Spring Boot 2.4.1**
+
+### Topjava 20
+- мигрировали на JDK 14
+- в `@SafeHtml` запрещаем весь html (`whitelistType = NONE`)
+- в `topjava.common.js` в `makeEditable()` вместо объекта контекст передаю 3 параметра
+- в UI контроллерах убрал префикс `ajax`
+- из тестов сервисов убрал `repository`. При проверке через `assertThrows` он не требуется
+- в `TestMatcher` сценарии сравнения сделал параметризируемыми (паттерн стратегия)
+- в API добавили `/users/{id}/with-meals` (см. [двунаправленные отношения](https://www.codeflow.site/ru/article/jackson-bidirectional-relationships-and-infinite-recursion))
+- добавил `UserTestData.USER_WITH_MEALS_MATCHER` (проверки пользователя сразу с едой) и константу id `NOT_FOUND`
+
+### Topjava 19
+- Изменилась логика для интервалов времени (исключаем `endTime`)
+- Заменил собственный `MessageUtil` велосипед на спринговый `MessageSourceAccessor`
+- В ролях убрал префиксы `ROLE_` ([Role and GrantedAuthority](https://stackoverflow.com/a/19542316/548473))
+- Добавился удобный метод `int AbstractBaseEntity.id()`
+- Фикс `Location` в `ProfileRestController.register`
+- Фикс валидации `UniqueMailValidator` для REST update без `user.id`
+- Заменил `jdbc.initLocation` на полный путь - IDEA не ругается
+- В конфигурации `cargo-maven2-plugin` сделал [индивидуальный контекст приложения](https://stackoverflow.com/a/60797999/548473)
+- Тесты
+ - Обновил даты еды на 2020г.
+ - Зарефакторил тесты сервисов на удаление - `NotFoundException` может бросаться при `delete()`
+ - В тестах контроллеров вернулся к реализации без обертки над `MockMvcRequestBuilders`
+ - Для `InMemory` тестов подключаю только `inmemory.xml` (добавил туда необходимую конфигурацию из `spring-app.xml`)
+
+
### Topjava 18
- В `ErrorType` добавил `HttpStatus status`
-- В PostgreSQL обнаружилась бага: граничное значение `0:00` из за ошибок округления попадает в предыдущий интервал.
+- В PostgreSQL обнаружилась бага: граничное значение `0:00` из-за ошибок округления попадает в предыдущий интервал.
Мораль: всегда в тестах проверяйте граничные значения. Добавил этот случай в тестовые данные.
- Изменил `MealRepository.getBetween` (принимаю `@Nullable LocalDate`). Изменились реализации.
- Выделил метод `UserService.prepareAndSave`
diff --git a/config/Topjava-soapui-project.xml b/config/Topjava-soapui-project.xml
new file mode 100644
index 000000000000..b66be7438670
--- /dev/null
+++ b/config/Topjava-soapui-project.xml
@@ -0,0 +1,610 @@
+
+
+ * Handling Hibernate lazy-loading
+ *
+ * @link https://github.com/FasterXML/jackson
+ * @link https://github.com/FasterXML/jackson-datatype-hibernate
+ * @link https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers
+ */
+public class JacksonObjectMapper extends ObjectMapper {
+
+ private static final ObjectMapper MAPPER = new JacksonObjectMapper();
+
+ private JacksonObjectMapper() {
+ registerModule(new Hibernate5Module());
+
+ registerModule(new JavaTimeModule());
+ configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+
+ setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
+ setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
+ setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ }
+
+ public static ObjectMapper getMapper() {
+ return MAPPER;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java
new file mode 100644
index 000000000000..d3bf1ac0435f
--- /dev/null
+++ b/src/main/java/ru/javawebinar/topjava/web/json/JsonUtil.java
@@ -0,0 +1,49 @@
+package ru.javawebinar.topjava.web.json;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectReader;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static ru.javawebinar.topjava.web.json.JacksonObjectMapper.getMapper;
+
+public class JsonUtil {
+
+ public static
+ * Comparing actual and expected objects via AssertJ
+ * Support converting json MvcResult to objects for comparation.
+ */
+public class MatcherFactory {
+
+ public static Filter separately
+ *
+ */
+ public List
+
+
+ ${status}
+
+ ${message}
+
+ Spring Security,
+ Spring MVC,
+ Spring Data JPA,
+ Spring Security
+ Test,
+ Hibernate ORM,
+ Hibernate Validator,
+ SLF4J,
+ Json Jackson,
+ JSP,
+ JSTL,
+ Apache Tomcat,
+ WebJars,
+ DataTables,
+ EHCACHE,
+ PostgreSQL,
+ HSQLDB,
+ JUnit 5,
+ Hamcrest,
+ AssertJ,
+ jQuery,
+ jQuery plugins,
+ Bootstrap.
+
+ <%--https://getbootstrap.com/docs/4.0/components/card/--%>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${userTo.name}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
" + errorInfo.type + "
" + errorInfo.detail,
+ type: "error",
+ layout: "bottomRight"
+ });
+ failedNote.show()
+}
\ No newline at end of file
diff --git a/src/main/webapp/resources/js/topjava.meals.js b/src/main/webapp/resources/js/topjava.meals.js
new file mode 100644
index 000000000000..295dc3aa0756
--- /dev/null
+++ b/src/main/webapp/resources/js/topjava.meals.js
@@ -0,0 +1,119 @@
+const mealAjaxUrl = "profile/meals/";
+
+// https://stackoverflow.com/a/5064235/548473
+const ctx = {
+ ajaxUrl: mealAjaxUrl,
+ updateTable: function () {
+ $.ajax({
+ type: "GET",
+ url: mealAjaxUrl + "filter",
+ data: $("#filter").serialize()
+ }).done(updateTableByData);
+ }
+};
+
+function clearFilter() {
+ $("#filter")[0].reset();
+ $.get(mealAjaxUrl, updateTableByData);
+}
+
+// http://api.jquery.com/jQuery.ajax/#using-converters
+$.ajaxSetup({
+ converters: {
+ "text json": function (stringData) {
+ var json = JSON.parse(stringData);
+ if (typeof json === 'object') {
+ $(json).each(function () {
+ if (this.hasOwnProperty('dateTime')) {
+ this.dateTime = this.dateTime.substr(0, 16).replace('T', ' ');
+ }
+ });
+ }
+ return json;
+ }
+ }
+});
+
+$(function () {
+ makeEditable({
+ "columns": [
+ {
+ "data": "dateTime"
+ },
+ {
+ "data": "description"
+ },
+ {
+ "data": "calories"
+ },
+ {
+ "render": renderEditBtn,
+ "defaultContent": "",
+ "orderable": false
+ },
+ {
+ "render": renderDeleteBtn,
+ "defaultContent": "",
+ "orderable": false
+ }
+ ],
+ "order": [
+ [
+ 0,
+ "desc"
+ ]
+ ],
+ "createdRow": function (row, data, dataIndex) {
+ $(row).attr("data-meal-excess", data.excess);
+ }
+ });
+
+// http://xdsoft.net/jqplugins/datetimepicker/
+ var startDate = $('#startDate');
+ var endDate = $('#endDate');
+ startDate.datetimepicker({
+ timepicker: false,
+ format: 'Y-m-d',
+ formatDate: 'Y-m-d',
+ onShow: function (ct) {
+ this.setOptions({
+ maxDate: endDate.val() ? endDate.val() : false
+ })
+ }
+ });
+ endDate.datetimepicker({
+ timepicker: false,
+ format: 'Y-m-d',
+ formatDate: 'Y-m-d',
+ onShow: function (ct) {
+ this.setOptions({
+ minDate: startDate.val() ? startDate.val() : false
+ })
+ }
+ });
+
+ var startTime = $('#startTime');
+ var endTime = $('#endTime');
+ startTime.datetimepicker({
+ datepicker: false,
+ format: 'H:i',
+ onShow: function (ct) {
+ this.setOptions({
+ maxTime: endTime.val() ? endTime.val() : false
+ })
+ }
+ });
+ endTime.datetimepicker({
+ datepicker: false,
+ format: 'H:i',
+ onShow: function (ct) {
+ this.setOptions({
+ minTime: startTime.val() ? startTime.val() : false
+ })
+ }
+ });
+
+ $('#dateTime').datetimepicker({
+ format: 'Y-m-d H:i'
+ });
+});
\ No newline at end of file
diff --git a/src/main/webapp/resources/js/topjava.users.js b/src/main/webapp/resources/js/topjava.users.js
new file mode 100644
index 000000000000..47b38fe4ce8a
--- /dev/null
+++ b/src/main/webapp/resources/js/topjava.users.js
@@ -0,0 +1,86 @@
+const userAjaxUrl = "admin/users/";
+
+// https://stackoverflow.com/a/5064235/548473
+const ctx = {
+ ajaxUrl: userAjaxUrl,
+ updateTable: function () {
+ $.get(userAjaxUrl, updateTableByData);
+ }
+}
+
+function enable(chkbox, id) {
+ var enabled = chkbox.is(":checked");
+// https://stackoverflow.com/a/22213543/548473
+ $.ajax({
+ url: userAjaxUrl + id,
+ type: "POST",
+ data: "enabled=" + enabled
+ }).done(function () {
+ chkbox.closest("tr").attr("data-user-enabled", enabled);
+ successNoty(enabled ? "Enabled" : "Disabled");
+ }).fail(function () {
+ $(chkbox).prop("checked", !enabled);
+ });
+}
+
+// $(document).ready(function () {
+$(function () {
+ makeEditable({
+ "columns": [
+ {
+ "data": "name"
+ },
+ {
+ "data": "email",
+ "render": function (data, type, row) {
+ if (type === "display") {
+ return "" + data + "";
+ }
+ return data;
+ }
+ },
+ {
+ "data": "roles"
+ },
+ {
+ "data": "enabled",
+ "render": function (data, type, row) {
+ if (type === "display") {
+ return "";
+ }
+ return data;
+ }
+ },
+ {
+ "data": "registered",
+ "render": function (date, type, row) {
+ if (type === "display") {
+ return date.substring(0, 10);
+ }
+ return date;
+ }
+ },
+ {
+ "orderable": false,
+ "defaultContent": "",
+ "render": renderEditBtn
+ },
+ {
+ "orderable": false,
+ "defaultContent": "",
+ "render": renderDeleteBtn
+ }
+ ],
+ "order": [
+ [
+ 0,
+ "asc"
+ ]
+ ],
+ "createdRow": function (row, data, dataIndex) {
+ if (!data.enabled) {
+ $(row).attr("data-user-enabled", false);
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/src/main/webapp/test.html b/src/main/webapp/test.html
new file mode 100644
index 000000000000..e50b3327754f
--- /dev/null
+++ b/src/main/webapp/test.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java
new file mode 100644
index 000000000000..da3bec700df3
--- /dev/null
+++ b/src/test/java/ru/javawebinar/topjava/ActiveDbProfileResolver.java
@@ -0,0 +1,19 @@
+package ru.javawebinar.topjava;
+
+import org.springframework.lang.NonNull;
+import org.springframework.test.context.support.DefaultActiveProfilesResolver;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+//http://stackoverflow.com/questions/23871255/spring-profiles-simple-example-of-activeprofilesresolver
+public class ActiveDbProfileResolver extends DefaultActiveProfilesResolver {
+ @Override
+ public @NonNull
+ String[] resolve(@NonNull Class> aClass) {
+ List