SlideShare una empresa de Scribd logo
1 de 51
Descargar para leer sin conexión
Національний університет кораблебудування імені адмірала Макарова
Навчально-науковий інститут комп'ютерних наук та управління проектами
КУРСОВИЙ ПРОЕКТ (РОБОТА)
з ” Технології розробки програмного забезпечення на
сучасних платформах”
(назва дисципліни)
на тему: Онлайн бібліотека
Студента II курсу 2151 групи спеціальності 121
Гурмази.М.В
(прізвище та ініціали)
Керівник cтарший викладачі,
Беркунський Є.Ю,
Смикодуб.Т.Г
Національна шкала ________________
Кількість балів: __________Оцінка: ECTS _____
Члени комісії ________________ Беркунський Є.Ю
(підпис)
________________ Смикодуб.Т.Г
(підпис)
АНОТАЦІЯ
У даній курсовій роботі розглядається проєкт WEB додатку “Онлайн
Бібліотека”. Робота виконана на 50 сторінках, містить 8 рисунків. Робота
містить 1 програму, з текстом програми та результатами. Робота виконана
українською мовою.
ABSTRACT
This course work represents the WEB application project "Online library)". The
work is completed on 50 pages, contains 8 drawings. The work contains 1
program, with program text and results. The work was done in Ukrainian.
ЗМІСТ
ВСТУП…………………………………………………………………....4
1. АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ …… ………………………………....5
2. ПОСТАНОВКА ЗАДАЧІ……… ….…………………………………………..6
3. ДІАГРАМА СУТНІСТЬ-ЗВ'ЯЗОК…….…………………………………….6
4. ДІАГРАМА КЛАСІВ……..………………………………………………………7
5. ДІАГРАМА ПОСЛІДОВНОСТІ…..…………………………………………8
6. ДІАГРАМА СТАНІВ…..…………………………………………………………9
7. ЗАСОБИ РОЗРОБКИ……………………….…………………………………..10
8. РЕЗУЛЬТАТИ РОЗРОБКИ…………………….……………………………..13
ВИСНОВКИ…………………………………………………………………………....45
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ…………………………...……..…..46
ДОДАТОК А…………………………………………………………….…...………..47
2
ВСТУП
При наявності великої кількості користувачів та книг у бібліотеці важливо
мати зручні методи обслуговування та ефективний облік доступних книг та
користувачів. Методом обслуговування для користувачів може бути
можливість читання книг, а адміністраторам необхідно мати можливість
редагування та додавання нових книг.
Для ведення обліку доступних книг та користувачів ідеально підходять бази
даних, де інформація зберігається в зрозумілому та зручному форматі, з
можливістю легкої маніпуляції (додавання/видалення записів, редагування
даних).
Як предметну область я обрав "Онлайн бібліотеку", для якої необхідно
створити веб-додаток з можливістю використання бази даних для обліку
доступних книг та користувачів. Ця база даних може містити наступну
інформацію: назву книги, автора, видавницвто, жанр, рік видання, опис,
кількість сторінок, текст.
Онлайн бібліотека повинна надавати користувачам можливість перегляду
доступних книг, пошуку за різними критеріями, перегляду деталей про книгу
та читання її. Адміністратори повинні мати можливість редагування існуючих
книг, додавання нових книг.
3
АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ
Онлайн бібліотека – це електронний сервіс, який надає користувачам доступ
до електронних книг та інших текстових матеріалів через Інтернет. Зазвичай
онлайн бібліотеки дозволяють користувачам шукати та переглядати книги.
Робота онлайн бібліотеки може бути організована так: користувач
реєструється на веб-сайті, після чого отримує доступ до каталогу електронних
книг. Користувач може шукати книги за назвою та жанром, вибирати книги
для читання.
Як адміністратор онлайн бібліотеки може виступати бібліотекар, який керує
каталогом електронних книг та забезпечує доступність книг для користувачів.
Адміністратор може додавати нові книги до каталогу, змінювати інформацію
про книги, видаляти книги. Для забезпечення безпеки та конфіденційності
даних користувача онлайн бібліотека може використовувати систему
реєстрації та авторизації користувачів, а також захист даних з використанням
шифрування. Також може бути реалізована система контролю доступу, яка
дозволяє обмежувати доступ до книг залежно від статусу користувача.
Таким чином, онлайн бібліотека дозволяє користувачам насолоджуватися
читанням книг у зручний час та місце, а адміністратору – забезпечувати
доступність та оновлення каталогу електронних книг.
4
ПОСТАНОВКА ЗАДАЧІ
Завдання полягає у створенні веб-додатку для надання послуг онлайн
бібліотеки. Веб-додаток повинен мати привабливий та легко сприйнятний
графічний інтерфейс користувача. Він має включати перелік доступних послуг
та можливість користувачам реєструватися та отримувати доступ до
електронних книг.
Основні функціональні можливості веб-додатку для онлайн бібліотеки
включають:
1. Реєстрація користувачів: Користувачі можуть створювати облікові записи в
системі, вводячи необхідні дані, такі як ім'я, електронна адреса та пароль.
2. Авторизація та аутентифікація: Користувачі можуть увійти в систему за
допомогою свого облікового запису, використовуючи введений при
реєстрації пароль. Система повинна забезпечити безпеку даних та
обмеження доступу до функцій, що призначені тільки для адміністратора.
3. Перегляд каталогу книг: Користувачі можуть переглядати каталог
електронних книг, використовуючи різні фільтри, такі як назва, автор,
жанр, рік видання і т.д. Кожна книга повинна мати відповідну сторінку з
описом, обкладинкою та можливістю перегляду додаткових деталей.
4. Пошук книг: Користувачі можуть використовувати пошукову функцію для
знаходження конкретної книги.
5. Читання книг: Користувачі мають можливість відкривати електронні книги
для читання в онлайн-режимі.
5
ДІАГРАМА КЛАСІВ
Рисунок 1 — діаграма класів
6
ДІАГРАМА СУТНІСТЬ-ЗВ’ЯЗОК
Рисунок 2 – діаграма сутність-зв’язок
7
ДІАГРАМА ПОСЛІДОВНОСТІ
Діаграма послідовності для класу BookController.java представлена на
рисунку 3.
8
ДІАГРАМА ПОСЛІДОВНОСТІ
Діаграма послідовності для класу UserService.java представлена на рисунку 4.
ДІАГРАМА CТАНІВ
Діаграма станів для класу UserService.java представлена на рисунку 5
9
ДІАГРАМА CТАНІВ
Діаграма станів для класу BookController.java представлена на рисунку 4.
10
ЗАСОБИ РОЗРОБКИ
Для розробки проєкту мною було обране програмне забезпечення
JetBrains IntelliJ IDEA є провідним середовищем швидкого розвитку мови
Java. IntelliJ IDEA — це високотехнологічний комплекс тісно інтегрованих
інструментів програмування, включаючи інтелектуальний редактор джерел з
передовими інструментами автоматизації, потужні інструменти рефакторингу
коду, вбудовану підтримку технологій J2EE, інтеграційні механізми з
середовищем тестування Ant/ JUnit та системами контролю версій,
унікальний інструмент оптимізації та перевірки перевірки коду, а також
інноваційний візуальний графічний інтерфейс. Унікальні особливості JetBrains
IntelliJ IDEA знімають з програміста тягар рутинної роботи, допомагають
своєчасно усунути помилки і поліпшити якість коду, піднявши продуктивність
розробника на нову висоту.
Мовою програмування була обрана Java 17 версії. Java —
об’єктноорієнтована мова програмування, випущена 1995 року компанією
«Sun Microsystems» як основний компонент платформи Java. З 2009 року
мовою займається компанія «Oracle», яка того року придбала «Sun
Microsystems». В офіційній реалізації Java-програми компілюються у байт-
код, який при виконанні інтерпретується віртуальною машиною для
конкретної платформи.
«Oracle» надає компілятор Java та віртуальну машину Java, які
задовольняють специфікації Java Community Process, під ліцензією GNU
General Public License.
Об'єктно–орієнтоване програмування (ООП) — це модель програмування
яка базується на стверджені того, що програма це сукупність об’єктів які
взаємодіють між собою. Кожен об’єкт в цій моделі є незалежним, і він
здатний отримувати, обробляти дані та відправляти ці дані іншим об’єктам. В
ООП використано моделі успадкування, модульності, поліморфізму та
інкапсуляції.
Maven — це інструмент побудови та управління проектами, який зазвичай
використовується в фреймворках, побудованих на Java. Він розроблений
Apache Software Foundation. Maven, слово з мови ідиш, означає «збирач
знань». Він був введений, щоб зробити процес запуску побудови в
Джакартському турбінному проекті.
11
Maven контролюється файлом Project Object Model (pom). Під час роботи з
вбудованими фреймворками Java нам часто доводиться мати справу з
низкою залежностей.
До того, як Maven з'явився в картині, усі залежності, які є не чим іншим, як
файлами JAR, повинні були бути додані в наш фреймворк вручну. Крім того,
нам потрібно було подбати про оновлення програмного забезпечення в
нашому проекті.
HTML (Hypertext Markup Language — Мова гіпертекстової розмітки) — це
мова опису структури сторінок документів, яка дозволяє звичайний текст
форматувати в абзаци, заголовки, списки та інші структури, створювати
посилання на інші сторінки. Це текстова мова, в якій інструкції з
форматування, що називаються тегами, вбудовані в розділи документа, які
містять конкретну інформацію. Теги повідомляють браузерам, як
форматувати і представляти інформацію на екрані.
CSS (абревіатура від Cascading Style Sheets, що в перекладі означає каскадні
таблиці стилів) — це спеціальна мова (мова стилів), за допомогою якої
описують вигляд документів (як і де відображати елементи вебсторінки),
написаних мовами розмітки даних. Найчастіше CSS використовується для
документів, котрі розмічені мовою HTML, XHTML та XML.
Lombok — це плагін компілятора, який додає в Java нові ключові слова і
перетворює анотації в Java-код, зменшуючи зусилля на розробку і
забезпечуючи деяку додаткову функціональність.
Spring MVC забезпечує архітектуру патерна Model - View - Controller
(Модель - Відображення - Контролер) — структура для створення слабо
пов'язаних веб-додатків, що розділяє основні аспекти їх розробки: об'єкти,
бізнес-логіку та зовнішній вигляд програми. Основна перевага архітектури
MVC — можливість міняти один із компонентів програми, суттєво не
впливаючи на інші.
Spring Security - це популярний модуль у Spring Framework, який забезпечує
аутентифікацію і авторизацію веб-додатків. Він додає безпеку на рівні URL,
методу або навіть на рівні доменних об'єктів.
12
MariaDB — відгалуження реляційної СУБД MySQL, що розробляється
спільнотою під ліцензією GPL. MariaDB повністю сумісна з програмами, що
використовують MySQL, а перехід на цю СУБД виправданий тим, що MySQL
вже не так активно розвивається. У MariaDB вбудовані покращений
оптимізатор запитів, безпечна та швидка реплікація, швидші індекси для
механізму зберігання даних MEMORY(HEAP), більш висока продуктивність
перекодування символів, використання пулу потоків, а також багато інших
покращень, що позитивно впливають на продуктивність.
Результати Розробки
Результати розробки загалом представлені на лістингах 1-20. Javaкласи
представлені на лістингах 1-11. Сторінки HTML на лістингах 12-18.
Сторінки css на лістингах 19-20.
Лістинг 1
OnLibApplication.java
package com.example.OnLib;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OnLibApplication {
public static void main(String[] args) {
SpringApplication.run(OnLibApplication.class, args);
}
}
Лістинг 2
SecurityConfig.java
package com.example.OnLib.configurations;
import com.example.OnLib.services.CustomUserDetailsService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import
org.springframework.security.config.annotation.authentication.builders.Authen
ticationManagerBuilder;
import
org.springframework.security.config.annotation.method.configuration.EnableGlo
balMethodSecurity;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSec
urity;
13
import
org.springframework.security.config.annotation.web.configuration.WebSecurityC
onfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomUserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/read", "/books-by-title")
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws
Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(8);
}
}
Лістинг 3
BookController.java
package com.example.OnLib.controllers;
import com.example.OnLib.dao.BookRepository;
import com.example.OnLib.entities.Book;
import lombok.AllArgsConstructor;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
14
import org.springframework.web.server.ResponseStatusException;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
@Controller
@AllArgsConstructor
public class BookController {
private BookRepository bookRepository;
@GetMapping("/")
public String showAllBooks(Model model) {
List<Book> books = bookRepository.findAll();
model.addAttribute("books", books);
return "mainPage";
}
@PostMapping("/books-by-title")
public String showBooksByAuthor(@RequestParam("title") String title,
Model model) {
List<Book> books = bookRepository.findBookByTitle(title);
model.addAttribute("books", books);
model.addAttribute("title", title);
return "index";
}
@PostMapping("/addBooks")
public String addBooks(@RequestParam String title,
@RequestParam String author,
@RequestParam String publisher,
@RequestParam String genre,
@RequestParam String description,
@RequestParam int number_Of_Pages,
@RequestParam int year_Of_Publication,
@RequestParam MultipartFile image,
@RequestParam String text) {
Book book = new Book();
book.setTitle(title);
book.setAuthor(author);
book.setPublisher(publisher);
book.setGenre(genre);
book.setDescription(description);
book.setNumberOfPages(number_Of_Pages);
book.setYearOfPublication(year_Of_Publication);
book.setText(text);
try {
book.setImage(image.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
bookRepository.save(book);
return "redirect:/";
}
@GetMapping("/deleteBook")
public String deleteBook (@RequestParam int id){
bookRepository.deleteById(id);
return "redirect:/";
}
@GetMapping("/editBook")
public String editBook(@RequestParam int id, Model model){
Optional <Book> optionalBook = bookRepository.findById(id);
if(optionalBook.isEmpty()){
return "redirect:/";
15
}
model.addAttribute("book", optionalBook.get());
return "edit";
}
@PostMapping("/updateBook")
public String updateBook (@RequestParam int id,
@RequestParam String title,
@RequestParam String author,
@RequestParam String publisher,
@RequestParam String genre,
@RequestParam String description,
@RequestParam int number_Of_Pages,
@RequestParam int year_Of_Publication,
@RequestParam MultipartFile image,
@RequestParam String text) {
Optional<Book> optionalBook = bookRepository.findById(id);
optionalBook.ifPresent(t -> {
t.setTitle(title);
t.setAuthor(author);
t.setPublisher(publisher);
t.setGenre(genre);
t.setDescription(description);
t.setNumberOfPages(number_Of_Pages);
t.setYearOfPublication(year_Of_Publication);
t.setText(text);
try {
t.setImage(image.getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
bookRepository.save(t);
});
return "redirect:/";
}
@GetMapping("/book/{id}/image")
public ResponseEntity<byte[]> getImage(@PathVariable Integer id) {
Book book = bookRepository.findById(id).orElseThrow(() -> new
ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found"));
byte[] image = book.getImage();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
return new ResponseEntity<>(image, headers, HttpStatus.OK);
}
@GetMapping("/read")
public String showBookText(Model model, @RequestParam("id") Integer
bookId) {
Book book = bookRepository.findById(bookId).orElse(null);
model.addAttribute("bookText", book.getText());
return "readBook";
}
@GetMapping("/romance-books")
public String showRomanceBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Роман");
model.addAttribute("books", books);
return "secondPage";
}
@GetMapping("/detective-books")
public String showDetectiveBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Детектив");
model.addAttribute("books", books);
return "secondPage";
16
}
@GetMapping("/drama-books")
public String showDramaBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Драма");
model.addAttribute("books", books);
return "secondPage";
} @GetMapping("/fantasy-books")
public String showFentBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Фэнтези");
model.addAttribute("books", books);
return "secondPage";
}
@GetMapping("/triller-books")
public String showTrillerBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Триллер/Ужасы");
model.addAttribute("books", books);
return "secondPage";
}
@GetMapping("/adventures-books")
public String showAdventureBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Приключения");
model.addAttribute("books", books);
return "secondPage";
}
@GetMapping("/psyh-books")
public String showPsyhBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Психология");
model.addAttribute("books", books);
return "secondPage";
}
@GetMapping("/fairy-books")
public String showFairyBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Сказки");
model.addAttribute("books", books);
return "secondPage";
}
@GetMapping("/classic-books")
public String showClassicBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Классика");
model.addAttribute("books", books);
return "secondPage";
}
@GetMapping("/mistik-books")
public String showMistikBooks(Model model) {
List<Book> books = bookRepository.findByGenre("Мистика");
model.addAttribute("books", books);
return "secondPage";
}
}
17
Лістинг 4
UserController.java
package com.example.OnLib.controllers;
import com.example.OnLib.entities.User;
import com.example.OnLib.services.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/mainPage")
public String mainPage() {
return "mainPage";
}
@GetMapping("/logout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/login?logout";
}
@GetMapping("/registration")
public String registration(Model model) {
model.addAttribute("user", new User());
return "registration";
}
@PostMapping("/registration")
public String createUser(User user, Model model) {
if (!userService.createUser(user)) {
model.addAttribute("errorMessage", "Пользователь с email: " +
user.getEmail() + " уже существует");
return "registration";
}
return "redirect:/login";
}
18
Лістинг 5
BookReoisitory.java
package com.example.OnLib.dao;
import com.example.OnLib.entities.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface BookRepository extends JpaRepository<Book, Integer> {
List<Book> findBookByTitle(String title);
List<Book> findByGenre(String genre);
}
Лістинг 6
UserRepoisitory.java
package com.example.OnLib.dao;
import com.example.OnLib.entities.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
}
Лістинг 7
Role.java
package com.example.OnLib.entities.enums;
import org.springframework.security.core.GrantedAuthority;
public enum Role implements GrantedAuthority {
ROLE_USER, ROLE_ADMIN;
@Override
public String getAuthority() {
return name();
}
}
19
Лістинг 8
Book.java
package com.example.OnLib.entities;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
@Getter
@Setter
@Entity
@Table(name = "book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
@Column(name = "title", length = 50)
private String title;
@Column(name = "author", length = 50)
private String author;
@Column(name = "publisher", length = 50)
private String publisher;
@Column(name = "yearOfPublication")
private Integer yearOfPublication;
@Column(name = "numberOfPages")
private Integer numberOfPages;
@Column(name = "image")
private byte[] image;
@Column(name = "genre")
private String genre;
@Column(name = "description")
private String description;
@Column(name = "text")
private String text;
}
Лістинг 9
User.java
package com.example.OnLib.entities;
import com.example.OnLib.entities.enums.Role;
import lombok.Data;
20
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.*;
@Entity
@Table(name = "users")
@Data
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "email", unique = true)
private String email;
@Column(name = "active")
private boolean active;
@Column(name = "password", length = 1000)
private String password;
@ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
@CollectionTable(name = "user_role",
joinColumns = @JoinColumn(name = "user_id"))
@Enumerated(EnumType.STRING)
private Set<Role> roles = new HashSet<>();
private LocalDateTime dateOfCreated;
@PrePersist
private void init() {
dateOfCreated = LocalDateTime.now();
}
// security
public boolean isAdmin() {
return roles.contains(Role.ROLE_ADMIN);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
@Override
public String getUsername() {
21
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return active;
}
}
Лістинг 10
CustomUserDetailsService.java
package com.example.OnLib.services;
import com.example.OnLib.dao.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import
org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws
UsernameNotFoundException {
return userRepository.findByEmail(email);
}}
22
Лістинг 11
UserService.java
package com.example.OnLib.services;
import com.example.OnLib.entities.User;
import com.example.OnLib.entities.enums.Role;
import com.example.OnLib.dao.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public boolean createUser(User user) {
String email = user.getEmail();
if (userRepository.findByEmail(email) != null) return false;
user.setActive(true);
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.getRoles().add(Role.ROLE_USER);
userRepository.save(user);
return true;
}
public List<User> list() {
return userRepository.findAll();
}
}
Лістинг 12
mainPage.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>OnLib</title>
<link th:rel="stylesheet" type="text/css"
th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}">
<link th:rel="stylesheet" type="text/css" th:href="style.css">
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
23
<div class="container-fluid">
<a class="navbar-brand" th:href="@{/}">OnLib</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-
label="Toggle navigation" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Books</a>
</li>
</ul>
<ul class="navbar-nav">
<div th:if="${#httpServletRequest.userPrincipal != null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/logout}">Logout</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal == null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/registration}">Register</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal == null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/login}">Login</a>
</li>
</div>
</ul>
</div>
</div>
</nav>
</header>
<form method="post" th:action="@{/books-by-title}">
<input type="text" id="titless" name="title" class="form-control"
placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2"
required>
<button type="submit" class="btn btn-outline-dark " >Искать</button>
</form>
<button type="button" class="btn btn btn-outline-danger" data-bs-
toggle="modal" data-bs-target="#exampleModal" data-bs-
whatever="@getbootstrap" sec:authorize="hasRole('ROLE_ADMIN')" >Добавить
книгу</button>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-
labelledby="exampleModalLabels" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabels" >Добавить
книгу</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-
label="Close"></button>
24
<form th:action="@{/addBooks}" method="post" enctype="multipart/form-
data">
<input type="text" id="titles" name="title"
placeholder="Название" aria-label="Название"/>
<input type="text" id="authors" name="author"
placeholder="Автор" aria-label="Автор"/>
<input type="text" id="publishers" name="publisher"
placeholder="Издательство" aria-label="Издательство"/>
<input type="text" id="genres" name="genre" placeholder="Жанр"
aria-label="Жанр"/>
<input type="text" id="descriptions" name="description"
placeholder="Описание" aria-label="Описание"/>
<input type="number" id="year_Of_Publications"
name="year_Of_Publication" placeholder="Год" aria-label="Год"/>
<input type="number" id="number_Of_Pagess" name="number_Of_Pages"
placeholder="Количество страниц" aria-label="Количество страниц"/>
<input type="file" id="images" name="image" accept="image/*"
required/>
<textarea id="text" name="text" placeholder="Текст" aria-
label="Текст"></textarea>
<button type="submit">Добавить</button>
</form>
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">Закрыть</button>
</div>
</div>
</div>
</div>
<div class="tab">
<table id="table" class="table table-striped table-sm table-hover">
<tbody>
<tr th:each="book : ${books} ">
<td>
<img th:src="@{/book/{id}/image(id=${book.id})}" class="img-fluid"
width="220" height="220" alt="Book Cover">
<p>
<span>Название: </span><span th:text="${book.title}"></span><br>
<span>Автор: </span><span th:text="${book.author}"></span><br>
<span>Издательство: </span><span
th:text="${book.publisher}"></span><br>
<span>Жанр: </span><span th:text="${book.genre}"></span><br>
<span>Год: </span><span th:text="${book.yearOfPublication
}"></span><br>
<span>Количество страниц: </span><span
th:text="${book.numberOfPages}"></span><br>
<a th:href="@{/read(id=${book.id})}" target="_blank" class="btn
btn-primary">Читать онлайн</a>
<a class="btn btn-danger" data-bs-toggle="modal" data-bs-
target="#myconfirm"
th:attr="data-bs-link=@{/deleteBook(id=${book.id})}, data-bs-
text=${book.title}, data-bs-id=${book.id}"
sec:authorize="hasRole('ROLE_ADMIN')">Удалить</a>
<a th:href="@{/editBook(id=${book.id})}" class="btn btn-success"
style="margin-bottom: 30px" sec:authorize="hasRole('ROLE_ADMIN')"
>Редактировать</a>
<div class="opys"><span>Описание: </span><span
th:text="${book.description}"></span>></div><br>
25
</p>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal fade" id="myconfirm" data-bs-backdrop="static" data-bs-
keyboard="false"
tabindex="-1" aria-labelledby="staticWarningLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="staticWarningLabel">Warning</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-
label="Close"></button>
</div>
<div class="modal-body">
Вы уверены что хотите удалить книгу?<br/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">
Отменить
</button>
<a class="btn btn-danger delete-button" style="margin-top:
30px">Удалить</a>
</div>
</div>
</div>
</div>
<script>
var myConfirmModal = document.getElementById('myconfirm')
myConfirmModal.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget
var link = button.getAttribute('data-bs-link')
var tid = button.getAttribute('data-bs-id')
var text = button.getAttribute('data-bs-text')
var modalTitle = myConfirmModal.querySelector('.modal-title')
modalTitle.textContent = 'Удалить книгу: ' + text + '(' + tid + ')'
var deleteButton = myConfirmModal.querySelector('.delete-button')
deleteButton.setAttribute("href", link)
})
</script>
<div style="width:140px;">
<td class="left" align="left" valign="top" width="202px">
<ul id="menu_left">
<li><a th:href="@{/romance-books}" title="Роман">Роман</a></li>
<li><a th:href="@{/detective-books}"
title="Детективы">Детективы</a></li>
<li><a th:href="@{/fantasy-books}" title="Фэнтези">Фэнтези</a></li>
<li><a th:href="@{/drama-books}" title="Драма">Драма</a></li>
<li><a th:href="@{triller-books}"
title="Триллер/Ужасы">Триллер/Ужасы</a></li>
<li><a th:href="@{/adventures-books}"
title="Приключения">Приключения</a></li>
<li><a th:href="@{/psyh-books}" title="Психология">Психология</a></li>
<li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li>
<li><a th:href="@{/classic-books}" title="Классика">Классика</a></li>
26
<li><a th:href="@{/mistik-books}" title="Мистика">Мистика</a></li>
</ul>
</td>
</div>
<script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script>
<script
th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script>
</body>
</html>
Лістинг 13
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<link th:rel="stylesheet" type="text/css"
th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-
BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
</head>
<body>
<style>
body{
background-color: #e2f1ff;
}
</style>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" >OnLib</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-
label="Toggle navigation" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Home</a>
</li>
</ul>
27
</div>
</div>
</nav>
</header>
<br>
<br>
<div class = "container">
<div class = "row">
<div class = "col-md-6 col-md-offset-3">
<h1> User Login Page </h1>
<form th:action="@{/login}" method="post">
<!-- error message -->
<div th:if="${param.error}">
<div class="alert alert-danger">Invalid username or
password.</div>
</div>
<!-- logout message -->
<div th:href="@{param.logout}">
<div class="alert alert-info" style="background-color: white">You
have been logged out.</div>
</div>
<div class = "form-group">
<label for ="username"> Username </label> :
<input type="text" class = "form-control" id ="username" name =
"username"
placeholder="Enter Email ID" autofocus="autofocus">
</div>
<div class="form-group">
<label for="password">Password</label>: <input type="password"
id="password"
name="password" class="form-control"
placeholder="Enter
Password" />
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<input type="submit" name="login-submit" id="login-submit"
class="form-control btn btn-primary" value="Log In" />
</div>
</div>
</div>
</form>
<div class="form-group">
<span>New user? <a href="/"
th:href="@{/registration}">Register
here</a></span>
</div>
</div>
</div>
</div>
</body>
</html>
28
Лістинг 14
index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>OnLib</title>
<link th:rel="stylesheet" type="text/css"
th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}">
<link th:rel="stylesheet" type="text/css" th:href="style.css"></head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/index">OnLib</a>
<button class="navbar-toggler" type="button" data-bs-
toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-
label="Toggle navigation" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Books</a>
</li>
</ul>
<ul class="navbar-nav">
<div th:if="${#httpServletRequest.userPrincipal !=
null}">
<li class="nav-item">
<a class="nav-link"
th:href="@{/logout}">Logout</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal ==
null}">
<li class="nav-item">
<a class="nav-link"
th:href="@{/registration}">Register</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal ==
null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/login}">Login</a>
</li>
</div>
</ul>
</div>
</div>
29
</nav>
</header>
<form method="post" th:action="@{/books-by-title}">
<input type="text" id="titles" name="title" class="form-control"
placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2"
required>
<button type="submit" class="btn btn-outline-dark " >Искать</button>
</form>
<button type="button" class="btn btn btn-outline-danger" data-bs-
toggle="modal" data-bs-target="#exampleModal" data-bs-
whatever="@getbootstrap" sec:authorize="hasRole('ROLE_ADMIN')" >Добавить
книгу</button>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-
labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel" >Добавить
книгу</h1>
<button type="button" class="btn-close" data-bs-
dismiss="modal" aria-label="Close"></button>
<form th:action="@{/addBooks}" method="post"
enctype="multipart/form-data">
<input type="text" id="title" name="title"
placeholder="Название" aria-label="Название"/>
<input type="text" id="author" name="author"
placeholder="Автор" aria-label="Автор"/>
<input type="text" id="publisher" name="publisher"
placeholder="Издательство" aria-label="Издательство"/>
<input type="text" id="genre" name="genre"
placeholder="Жанр" aria-label="Жанр"/>
<input type="text" id="description" name="description"
placeholder="Описание" aria-label="Описание"/>
<input type="number" id="year_Of_Publication"
name="year_Of_Publication" placeholder="Год" aria-label="Год"/>
<input type="number" id="number_Of_Pages"
name="number_Of_Pages" placeholder="Количество страниц" aria-
label="Количество страниц"/>
<input type="file" id="image" name="image"
accept="image/*" required/>
<textarea id="text" name="text" placeholder="Текст" aria-
label="Текст"></textarea>
<button type="submit">Add Book</button>
</form>
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="tab">
<table id="table" class="table table-striped table-sm table-hover">
<tbody>
<tr th:each="book : ${books} ">
<td>
<img th:src="@{/book/{id}/image(id=${book.id})}" class="img-
fluid" width="220" height="220" alt="Book Cover">
<p>
<span>Название: </span><span
30
th:text="${book.title}"></span><br>
<span>Автор: </span><span
th:text="${book.author}"></span><br>
<span>Издательство: </span><span
th:text="${book.publisher}"></span><br>
<span>Жанр: </span><span
th:text="${book.genre}"></span><br>
<span>Год: </span><span th:text="${book.yearOfPublication
}"></span><br>
<span>Количество страниц: </span><span
th:text="${book.numberOfPages}"></span><br>
<a th:href="@{/read(id=${book.id})}" target="_blank"
class="btn btn-primary">Читать онлайн</a>
<a class="btn btn-danger" data-bs-toggle="modal" data-bs-
target="#myconfirm"
th:attr="data-bs-link=@{/deleteBook(id=${book.id})},
data-bs-text=${book.title}, data-bs-id=${book.id}"
sec:authorize="hasRole('ROLE_ADMIN')">Удалить</a>
<a th:href="@{/editBook(id=${book.id})}" class="btn btn-
success" style="margin-bottom: 30px" sec:authorize="hasRole('ROLE_ADMIN')"
>Редактировать</a>
<div class="opys"><span>Описание: </span><span
th:text="${book.description}"></span>></div><br>
</p>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal fade" id="myconfirm" data-bs-backdrop="static" data-bs-
keyboard="false"
tabindex="-1" aria-labelledby="staticWarningLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5"
id="staticWarningLabel">Warning</h1>
<button type="button" class="btn-close" data-bs-
dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Вы уверены что хотите удалить книгу?<br/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">
Отменить
</button>
<a class="btn btn-danger delete-button" style="margin-top:
30px">Удалить</a>
</div>
</div>
</div>
</div>
<script>
var myConfirmModal = document.getElementById('myconfirm')
myConfirmModal.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget
31
var link = button.getAttribute('data-bs-link')
var tid = button.getAttribute('data-bs-id')
var text = button.getAttribute('data-bs-text')
var modalTitle = myConfirmModal.querySelector('.modal-title')
modalTitle.textContent = 'Удалить книгу: ' + text + '(' + tid + ')'
var deleteButton = myConfirmModal.querySelector('.delete-button')
deleteButton.setAttribute("href", link)
})
</script>
<div style="width:140px;">
<td class="left" align="left" valign="top" width="202px">
<ul id="menu_left">
<li><a th:href="@{/romance-books}" title="Роман">Роман</a></li>
<li><a th:href="@{/detective-books}"
title="Детективы">Детективы</a></li>
<li><a th:href="@{/fantasy-books}" title="Фэнтези">Фэнтези</a></li>
<li><a th:href="@{/drama-books}" title="Драма">Драма</a></li>
<li><a th:href="@{triller-books}"
title="Триллер/Ужасы">Триллер/Ужасы</a></li>
<li><a th:href="@{/adventures-books}"
title="Приключения">Приключения</a></li>
<li><a th:href="@{/psyh-books}"
title="Психология">Психология</a></li>
<li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li>
<li><a th:href="@{/classic-books}"
title="Классика">Классика</a></li>
<li><a th:href="@{/mistik-books}" title="Мистика">Мистика</a></li>
</ul>
</td>
</div>
<script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script>
<script
th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script>
</body>
</html>
Лістинг 15
edit.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>OnLib</title>
<link th:rel="stylesheet" type="text/css"
th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}">
<link th:rel="stylesheet" type="text/css" th:href="edit.css">
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" th:href="@{/}">OnLib</a>
32
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-
label="Toggle navigation" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Books</a>
</li>
</ul>
<ul class="navbar-nav">
<div th:if="${#httpServletRequest.userPrincipal != null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/logout}">Logout</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal == null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/registration}">Register</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal == null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/login}">Login</a>
</li>
</div>
</ul>
</div>
</div>
</nav>
</header>
<form th:action="@{/updateBook}" method="post" enctype="multipart/form-data">
<input type="hidden" id="id" name="id" th:value="${book.id}" />
<input type="text" id="title" name="title" placeholder="Название" aria-
label="Название" th:value="${book.title}" />
<input type="text" id="author" name="author" placeholder="Автор" aria-
label="Автор" th:value="${book.author}" />
<input type="text" id="publisher" name="publisher"
placeholder="Издательство" aria-label="Издательство"
th:value="${book.publisher}" />
<input type="text" id="genre" name="genre" placeholder="Жанр" aria-
label="Жанр" th:value="${book.genre}" />
<input type="text" id="description" name="description"
placeholder="Описание" aria-label="Описание" th:value="${book.description}"
/>
<input type="number" id="year_Of_Publication" name="year_Of_Publication"
placeholder="Год" aria-label="Год" th:value="${book.yearOfPublication}" />
<input type="number" id="number_Of_Pages" name="number_Of_Pages"
placeholder="Количество страниц" aria-label="Количество страниц"
th:value="${book.numberOfPages}" />
<input type="file" id="image" name="image" accept="image/*" />
<textarea id="text" name="text" placeholder="Текст" aria-label="Текст"
th:text="${book.text}"></textarea>
<button type="submit">Изменить</button>
</form>
</body>
33
<script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script>
<script
th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script>
</html>
Лістинг 16
readBook.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>OnLib</title>
<link th:rel="stylesheet" type="text/css"
th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}">
<link th:rel="stylesheet" type="text/css" th:href="style.css"></head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/index">OnLib</a>
<button class="navbar-toggler" type="button" data-bs-
toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-
label="Toggle navigation" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Books</a>
</li>
</ul>
<ul class="navbar-nav">
<div th:if="${#httpServletRequest.userPrincipal !=
null}">
<li class="nav-item">
<a class="nav-link"
th:href="@{/logout}">Logout</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal ==
null}">
<li class="nav-item">
<a class="nav-link"
th:href="@{/registration}">Register</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal ==
null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/login}">Login</a>
34
</li>
</div>
</ul>
</div>
</div>
</nav>
</header>
<form method="post" th:action="@{/books-by-title}">
<input type="text" id="title" name="title" class="form-control"
placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2"
required>
<button type="submit" class="btn btn-outline-dark " >Искать</button>
</form>
<div class="tab">
<p th:text="${bookText}"></p>
</div>
<div style="width:140px;">
<td class="left" align="left" valign="top" width="202px">
<ul id="menu_left">
<li><a th:href="@{/romance-books}" title="Роман">Роман</a></li>
<li><a th:href="@{/detective-books}"
title="Детективы">Детективы</a></li>
<li><a th:href="@{/fantasy-books}"
title="Фэнтези">Фэнтези</a></li>
<li><a th:href="@{/drama-books}" title="Драма">Драма</a></li>
<li><a th:href="@{triller-books}"
title="Триллер/Ужасы">Триллер/Ужасы</a></li>
<li><a th:href="@{/adventures-books}"
title="Приключения">Приключения</a></li>
<li><a th:href="@{/psyh-books}"
title="Психология">Психология</a></li>
<li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li>
<li><a th:href="@{/classic-books}"
title="Классика">Классика</a></li>
<li><a th:href="@{/mistik-books}"
title="Мистика">Мистика</a></li>
</ul>
</td>
</div>
<script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script>
<script
th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script>
</body>
</html>
Лістинг 17
readBook.html
<!DOCTYPE html>
<html>
<head>
<link th:rel="stylesheet" type="text/css"
th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}">
35
<meta charset="ISO-8859-1">
<title>Registration and Login App</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-
BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
</head>
<body>
<style>
body{
background-color: #e2f1ff;
}
</style>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" >OnLib</a>
<button class="navbar-toggler" type="button" data-bs-
toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-
label="Toggle navigation" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Home</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<br>
<br>
<div class = "container">
<div class = "row">
<div class = "col-md-6 col-md-offset-3">
<h1> User Registration Page </h1>
<form th:action="@{/registration}" th:object="${user}"
method="post">
<!-- error message -->
<div th:if="${errorMessage}">
<div class="alert alert-danger"
th:text="${errorMessage}"></div>
</div>
<div class = "form-group">
<label for ="email"> Email </label> :
<input type="email" class = "form-control" id ="email"
name = "email"
placeholder="Enter Email" autofocus="autofocus"
th:field="*{email}">
</div>
<div class="form-group">
36
<label for="password">Password</label>: <input
type="password"
id="password" name="password" class="form-control"
placeholder="Enter Password" th:field="*{password}"/>
</div>
<div class="form-group">
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<input type="submit" name="register-submit"
id="register-submit"
class="form-control btn btn-primary"
value="Register" />
</div>
</div>
</div>
</form>
<div class="form-group">
<span>Already registered? <a href="/login"
th:href="@{/login}">Log in
here</a></span>
</div>
</div>
</div>
</div>
</body>
</html>
Лістинг 18
readBook.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>OnLib</title>
<link th:rel="stylesheet" type="text/css"
th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}">
<link th:rel="stylesheet" type="text/css" th:href="style.css"></head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/index">OnLib</a>
<button class="navbar-toggler" type="button" data-bs-
toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-
label="Toggle navigation" >
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
37
<li class="nav-item">
<a class="nav-link" th:href="@{/}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Books</a>
</li>
</ul>
<ul class="navbar-nav">
<div th:if="${#httpServletRequest.userPrincipal !=
null}">
<li class="nav-item">
<a class="nav-link"
th:href="@{/logout}">Logout</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal ==
null}">
<li class="nav-item">
<a class="nav-link"
th:href="@{/registration}">Register</a>
</li>
</div>
<div th:if="${#httpServletRequest.userPrincipal ==
null}">
<li class="nav-item">
<a class="nav-link" th:href="@{/login}">Login</a>
</li>
</div>
</ul>
</div>
</div>
</nav>
</header>
<form method="post" th:action="@{/books-by-title}">
<input type="text" id="titles" name="title" class="form-control"
placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2"
required>
<button type="submit" class="btn btn-outline-dark " >Искать</button>
</form>
<button type="button" class="btn btn btn-outline-danger" data-bs-
toggle="modal" data-bs-target="#exampleModal" data-bs-
whatever="@getbootstrap" sec:authorize="hasRole('ROLE_ADMIN')" >Добавить
книгу</button>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-
labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel" >Добавить
книгу</h1>
<button type="button" class="btn-close" data-bs-
dismiss="modal" aria-label="Close"></button>
<form th:action="@{/addBooks}" method="post"
enctype="multipart/form-data">
<input type="text" id="title" name="title"
placeholder="Название" aria-label="Название"/>
<input type="text" id="author" name="author"
placeholder="Автор" aria-label="Автор"/>
<input type="text" id="publisher" name="publisher"
placeholder="Издательство" aria-label="Издательство"/>
38
<input type="text" id="genre" name="genre"
placeholder="Жанр" aria-label="Жанр"/>
<input type="text" id="description" name="description"
placeholder="Описание" aria-label="Описание"/>
<input type="number" id="year_Of_Publication"
name="year_Of_Publication" placeholder="Год" aria-label="Год"/>
<input type="number" id="number_Of_Pages"
name="number_Of_Pages" placeholder="Количество страниц" aria-
label="Количество страниц"/>
<input type="file" id="image" name="image"
accept="image/*" required/>
<textarea id="text" name="text" placeholder="Текст" aria-
label="Текст"></textarea>
<button type="submit">Add Book</button>
</form>
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="tab">
<table id="table" class="table table-striped table-sm table-hover">
<tbody>
<tr th:each="book : ${books} ">
<td>
<img th:src="@{/book/{id}/image(id=${book.id})}" class="img-
fluid" width="220" height="220" alt="Book Cover">
<p>
<span>Название: </span><span
th:text="${book.title}"></span><br>
<span>Автор: </span><span
th:text="${book.author}"></span><br>
<span>Издательство: </span><span
th:text="${book.publisher}"></span><br>
<span>Жанр: </span><span
th:text="${book.genre}"></span><br>
<span>Год: </span><span th:text="${book.yearOfPublication
}"></span><br>
<span>Количество страниц: </span><span
th:text="${book.numberOfPages}"></span><br>
<a th:href="@{/read(id=${book.id})}" target="_blank"
class="btn btn-primary">Читать онлайн</a>
<a class="btn btn-danger" data-bs-toggle="modal" data-bs-
target="#myconfirm"
th:attr="data-bs-link=@{/deleteBook(id=${book.id})},
data-bs-text=${book.title}, data-bs-id=${book.id}"
sec:authorize="hasRole('ROLE_ADMIN')">Удалить</a>
<a th:href="@{/editBook(id=${book.id})}" class="btn btn-
success" style="margin-bottom: 30px" sec:authorize="hasRole('ROLE_ADMIN')"
>Редактировать</a>
<div class="opys"><span>Описание: </span><span
th:text="${book.description}"></span>></div><br>
</p>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal fade" id="myconfirm" data-bs-backdrop="static" data-bs-
keyboard="false"
39
tabindex="-1" aria-labelledby="staticWarningLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5"
id="staticWarningLabel">Warning</h1>
<button type="button" class="btn-close" data-bs-
dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Вы уверены что хотите удалить книгу?<br/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-
dismiss="modal">
Отменить
</button>
<a class="btn btn-danger delete-button" style="margin-top:
30px">Удалить</a>
</div>
</div>
</div>
</div>
<script>
var myConfirmModal = document.getElementById('myconfirm')
myConfirmModal.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget
var link = button.getAttribute('data-bs-link')
var tid = button.getAttribute('data-bs-id')
var text = button.getAttribute('data-bs-text')
var modalTitle = myConfirmModal.querySelector('.modal-title')
modalTitle.textContent = 'Удалить книгу: ' + text + '(' + tid + ')'
var deleteButton = myConfirmModal.querySelector('.delete-button')
deleteButton.setAttribute("href", link)
})
</script>
<div style="width:140px;">
<td class="left" align="left" valign="top" width="202px">
<ul id="menu_left">
<li><a th:href="@{/romance-books}" title="Роман">Роман</a></li>
<li><a th:href="@{/detective-books}"
title="Детективы">Детективы</a></li>
<li><a th:href="@{/fantasy-books}"
title="Фэнтези">Фэнтези</a></li>
<li><a th:href="@{/drama-books}" title="Драма">Драма</a></li>
<li><a th:href="@{triller-books}"
title="Триллер/Ужасы">Триллер/Ужасы</a></li>
<li><a th:href="@{/adventures-books}"
title="Приключения">Приключения</a></li>
<li><a th:href="@{/psyh-books}"
title="Психология">Психология</a></li>
<li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li>
<li><a th:href="@{/classic-books}"
title="Классика">Классика</a></li>
<li><a th:href="@{/mistik-books}"
title="Мистика">Мистика</a></li>
</ul>
</td>
</div>
40
<script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script>
<script
th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script>
</body>
</html>
Лістинг 19
style.css
body{
background-color: #e2f1ff;
}
.form-control, .btn-outline-dark{
width: 300px;
margin-left: 300px;
margin-top: 10px;
}
.tab {
width: 1000px;
position: absolute;
height: 300px;
left: 50%;
transform: translate(-50%, -50%);
display: inline-block;
margin-top: 250px;
}
.tab p {
display: inline-block;
width: calc(100% - 270px);
vertical-align: top;
}
.btn-primary{
margin-bottom: 40px;
margin-top: 10px;
background-color: #1259a2;
}
.btn-outline-danger{
margin-top: 50px;
margin-left: 670px;
}
.btn-danger{
margin-bottom: 30px ;
}
#menu_left {
list-style: none;
margin: 0;
padding: 0;
position: fixed;
top: 50%;
left: 0;
transform: translateY(-50%);
display: inline-block;
41
background-color: #d4e1ff;
border: 1px solid ;
}
#menu_left li {
margin-bottom: 10px;
}
#menu_left li a {
display: block;
padding: 5px 10px;
color: #333;
text-decoration: none;
border-radius: 5px;
}
#menu_left li a:hover {
background-color: #ccc;
}
.modal-header {
display: flex;
flex-direction: column;
align-items: center;
}
.modal-header h1 {
margin: 0 0 20px 0;
}
.modal-header form {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.modal-header form input[type="text"],
.modal-header form input[type="number"],
.modal-header form textarea {
margin: 10px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
width: 300px;
font-size: 16px;
}
.modal-header form input[type="file"] {
margin: 10px;
padding: 10px;
font-size: 16px;
}
.modal-header form button[type="submit"],
.modal-header button[type="button"] {
margin: 10px;
padding: 10px 20px;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
42
}
.modal-header form button[type="submit"] {
background-color: #000000;
color: #fff;
border: none;
}
.modal-header button[type="button"] {
background-color: #fff;
color: #007bff;
border: 1px solid #007bff;
}
.modal-header form input[type="text"]:focus,
.modal-header form input[type="number"]:focus,
.modal-header form textarea:focus {
outline: none;
border: 1px solid #007bff;
}
Лістинг 20
edit.css
body{
background-color: #e2f1ff;
}
form {
margin: 20px auto;
padding: 20px;
background-color: #ffffff;
border: 1px solid #dee2e6;
border-radius: 5px;
width: 40%;
}
input,
textarea {
margin: 8px 0;
padding: 10px;
border: 1px solid #ced4da;
border-radius: 5px;
width: 100%;
}
button {
background-color: #0078fa;
color: #ffffff;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
display: block;
43
margin: 0 auto;
}
button:hover {
background-color: #0154ad;
}
label {
font-weight: bold;
}
textarea {
height: 150px;
}
input[type="file"] {
margin-top: 10px;
}
44
Висновки
Виконуючи цей проєкт, був розроблений web-додаток для обслуговування
клієнтів та зберігання інформації щодо наданих послуг онлайн бібліотеки.
Додаток забезпечує автоматизовану реєстрацію користувачів для отримання
доступу до бібліотечних ресурсів та зручний доступ для адміністратора для
керування бібліотекою.
Основні функціональні можливості додатку включають:
1. Реєстрація користувачів: Додаток дозволяє користувачам створювати
облікові записи, заповнюючи необхідні дані. Пошук та перегляд книг:
Користувачі можуть шукати книги за різними критеріями, такими як назва,
жанр. Після знаходження книги, користувач може переглянути її опис,
обкладинку та іншу відповідну інформацію.
2. Керування каталогом книг: Адміністратор бібліотеки має доступ до панелі
адміністрування, де він може додавати нові книги, редагувати існуючі
записи книг. Аутентифікація та авторизація: Додаток забезпечує механізми
аутентифікації користувачів для забезпечення безпеки доступу до
функціональності бібліотеки.
45
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ
1. Kathy Sierra, Bert Bates. Head first java, 2nd edition. O`REILLY 2012
2. Дінеж Раджпут. Spring. Всі паттерни програмування. Pakt 2019
3. Balaji Varanasi. Introducing Maven: A Build Tool for Today's Java Developers
2nd ed. Apress 2019
4. Використання бібліотеки Lombok:
веб-сайт. URL: https://projectlombok.org/features/
5. Приклади застосування spring: веб-сайт. URL: https://spring.io/guides
6. Застосування maven: веб-сайт. URL: https://maven.apache.org/
7. Використання шаблонізатора thymeleaf:
веб-сайт. URL: https://www.thymeleaf.org/documentation.html
46
ДОДАТОК А
Рисунок 1 — головна сторінка
Рисунок 2 — сторінка авторизації
47
Рисунок 3 — сторінка регістрації
Рисунок 4 — сторінка тексту книги
48
Рисунок 5 — сторінка аміністратора
Рисунок 6 — вікно додавання книги
49
Рисунок 7 — сторінка редагування книги
Рисунок 8 — вікно видалення книги
50

Más contenido relacionado

Similar a Курсовая (1).pdf

Урок 14. Засоби автоматизації створення текстового документа
Урок 14. Засоби автоматизації створення текстового  документа Урок 14. Засоби автоматизації створення текстового  документа
Урок 14. Засоби автоматизації створення текстового документа Василь Тереховський
 
веб 2.0 харків
веб 2.0  харківвеб 2.0  харків
веб 2.0 харківOlena Bashun
 
Презентація курсу "БД та ІС"
Презентація курсу "БД та ІС"Презентація курсу "БД та ІС"
Презентація курсу "БД та ІС"pogromskaya
 
проект бази даних для автоматизації роботи шкільної бібліотеки
проект бази даних для автоматизації роботи шкільної бібліотекипроект бази даних для автоматизації роботи шкільної бібліотеки
проект бази даних для автоматизації роботи шкільної бібліотекиfreedom_Z
 
Проект бази даних для автоматизації роботи шкільної бібліотеки (1).ppt
Проект бази даних для автоматизації роботи шкільної бібліотеки (1).pptПроект бази даних для автоматизації роботи шкільної бібліотеки (1).ppt
Проект бази даних для автоматизації роботи шкільної бібліотеки (1).pptfreedom_Z
 
презентация шмаков Диплом
презентация шмаков Дипломпрезентация шмаков Диплом
презентация шмаков ДипломСергей Шмаков
 
Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...
Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...
Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...johnnykramer
 
інф 11 кл веб сайт
інф 11 кл веб сайтінф 11 кл веб сайт
інф 11 кл веб сайтShool1
 
урок 26 огляд технологій веб 2
урок 26 огляд технологій веб 2урок 26 огляд технологій веб 2
урок 26 огляд технологій веб 2Helen Pata
 
БІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРС
БІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРСБІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРС
БІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРСDiana_LKTO
 
6 клас (урок№29)
6 клас (урок№29)6 клас (урок№29)
6 клас (урок№29)Sanya Dzhedzhera
 
Презинтація Щербак Ю.
Презинтація Щербак Ю.Презинтація Щербак Ю.
Презинтація Щербак Ю.Юлия Щербак
 
Інформатика, 10 клас, підручник (Морзе та ін.)
Інформатика, 10 клас, підручник (Морзе та ін.)Інформатика, 10 клас, підручник (Морзе та ін.)
Інформатика, 10 клас, підручник (Морзе та ін.)sveta7940
 

Similar a Курсовая (1).pdf (20)

Урок 14. Засоби автоматизації створення текстового документа
Урок 14. Засоби автоматизації створення текстового  документа Урок 14. Засоби автоматизації створення текстового  документа
Урок 14. Засоби автоматизації створення текстового документа
 
веб 2.0 харків
веб 2.0  харківвеб 2.0  харків
веб 2.0 харків
 
Презентація курсу "БД та ІС"
Презентація курсу "БД та ІС"Презентація курсу "БД та ІС"
Презентація курсу "БД та ІС"
 
проект бази даних для автоматизації роботи шкільної бібліотеки
проект бази даних для автоматизації роботи шкільної бібліотекипроект бази даних для автоматизації роботи шкільної бібліотеки
проект бази даних для автоматизації роботи шкільної бібліотеки
 
Проект бази даних для автоматизації роботи шкільної бібліотеки (1).ppt
Проект бази даних для автоматизації роботи шкільної бібліотеки (1).pptПроект бази даних для автоматизації роботи шкільної бібліотеки (1).ppt
Проект бази даних для автоматизації роботи шкільної бібліотеки (1).ppt
 
звіт
звітзвіт
звіт
 
презентация шмаков Диплом
презентация шмаков Дипломпрезентация шмаков Диплом
презентация шмаков Диплом
 
Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...
Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...
Ширенкова-Білійчук А.О. "Формування, розвиток і підтримка електронних колекці...
 
інф 11 кл веб сайт
інф 11 кл веб сайтінф 11 кл веб сайт
інф 11 кл веб сайт
 
урок 26 огляд технологій веб 2
урок 26 огляд технологій веб 2урок 26 огляд технологій веб 2
урок 26 огляд технологій веб 2
 
Web 2 0
Web 2 0Web 2 0
Web 2 0
 
Web 2 0
Web 2 0Web 2 0
Web 2 0
 
Web 2 0
Web 2 0Web 2 0
Web 2 0
 
Інтернет
ІнтернетІнтернет
Інтернет
 
БІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРС
БІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРСБІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРС
БІБЛІОТЕКАР І ЙОГО ВЕБ-РЕСУРС
 
6 клас (урок№29)
6 клас (урок№29)6 клас (урок№29)
6 клас (урок№29)
 
пз
пзпз
пз
 
Презинтація Щербак Ю.
Презинтація Щербак Ю.Презинтація Щербак Ю.
Презинтація Щербак Ю.
 
Інформатика, 10 клас, підручник (Морзе та ін.)
Інформатика, 10 клас, підручник (Морзе та ін.)Інформатика, 10 клас, підручник (Морзе та ін.)
Інформатика, 10 клас, підручник (Морзе та ін.)
 
10
1010
10
 

Más de ssuser0562f1

Алгоритмизация и программирование С/С++
Алгоритмизация и  программирование С/С++Алгоритмизация и  программирование С/С++
Алгоритмизация и программирование С/С++ssuser0562f1
 
Algorithms and programming lecture in ru
Algorithms and programming lecture in ruAlgorithms and programming lecture in ru
Algorithms and programming lecture in russuser0562f1
 
Geometry algorithms and formulas calculation
Geometry algorithms and formulas calculationGeometry algorithms and formulas calculation
Geometry algorithms and formulas calculationssuser0562f1
 
Algorithms in number theory presentation
Algorithms in number theory presentationAlgorithms in number theory presentation
Algorithms in number theory presentationssuser0562f1
 
springdatajpatwjug-120527215242-phpapp02.pdf
springdatajpatwjug-120527215242-phpapp02.pdfspringdatajpatwjug-120527215242-phpapp02.pdf
springdatajpatwjug-120527215242-phpapp02.pdfssuser0562f1
 
springdatajpa-up.pdf
springdatajpa-up.pdfspringdatajpa-up.pdf
springdatajpa-up.pdfssuser0562f1
 
08-170327133157.pdf
08-170327133157.pdf08-170327133157.pdf
08-170327133157.pdfssuser0562f1
 

Más de ssuser0562f1 (14)

Алгоритмизация и программирование С/С++
Алгоритмизация и  программирование С/С++Алгоритмизация и  программирование С/С++
Алгоритмизация и программирование С/С++
 
Algorithms and programming lecture in ru
Algorithms and programming lecture in ruAlgorithms and programming lecture in ru
Algorithms and programming lecture in ru
 
Geometry algorithms and formulas calculation
Geometry algorithms and formulas calculationGeometry algorithms and formulas calculation
Geometry algorithms and formulas calculation
 
Algorithms in number theory presentation
Algorithms in number theory presentationAlgorithms in number theory presentation
Algorithms in number theory presentation
 
jpa_nus.pdf
jpa_nus.pdfjpa_nus.pdf
jpa_nus.pdf
 
0808.pdf
0808.pdf0808.pdf
0808.pdf
 
servlets1.pdf
servlets1.pdfservlets1.pdf
servlets1.pdf
 
servlets.pdf
servlets.pdfservlets.pdf
servlets.pdf
 
springdatajpatwjug-120527215242-phpapp02.pdf
springdatajpatwjug-120527215242-phpapp02.pdfspringdatajpatwjug-120527215242-phpapp02.pdf
springdatajpatwjug-120527215242-phpapp02.pdf
 
springdatajpa-up.pdf
springdatajpa-up.pdfspringdatajpa-up.pdf
springdatajpa-up.pdf
 
08-170327133157.pdf
08-170327133157.pdf08-170327133157.pdf
08-170327133157.pdf
 
waits.pdf
waits.pdfwaits.pdf
waits.pdf
 
waits.pdf
waits.pdfwaits.pdf
waits.pdf
 
geometry.pdf
geometry.pdfgeometry.pdf
geometry.pdf
 

Último

Бібліотека – розвиток дитячої творчості та дозвілля для дітейpptx
Бібліотека – розвиток дитячої творчості  та дозвілля для дітейpptxБібліотека – розвиток дитячої творчості  та дозвілля для дітейpptx
Бібліотека – розвиток дитячої творчості та дозвілля для дітейpptxssuserc301ed1
 
Р.Шеклі "Запах думки". Аналіз оповідання
Р.Шеклі "Запах думки". Аналіз оповіданняР.Шеклі "Запах думки". Аналіз оповідання
Р.Шеклі "Запах думки". Аналіз оповіданняAdriana Himinets
 
атестація 2023-2024 Kewmrbq wtynh GNJ.pdf
атестація 2023-2024 Kewmrbq wtynh GNJ.pdfатестація 2023-2024 Kewmrbq wtynh GNJ.pdf
атестація 2023-2024 Kewmrbq wtynh GNJ.pdfhome
 
Презентациія для сайта Група «Незабудка».pptx
Презентациія для сайта Група «Незабудка».pptxПрезентациія для сайта Група «Незабудка».pptx
Презентациія для сайта Група «Незабудка».pptxOlgaDidenko6
 
ЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.ppt
ЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.pptЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.ppt
ЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.pptssuser59e649
 
Іваніщук Надія Вікторівна атестація .pdf
Іваніщук Надія Вікторівна атестація  .pdfІваніщук Надія Вікторівна атестація  .pdf
Іваніщук Надія Вікторівна атестація .pdfhome
 
Застосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdf
Застосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdfЗастосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdf
Застосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdfssuser15a891
 
Принципові відмінності досконалої (повної) конкуренції від інших форм організ...
Принципові відмінності досконалої (повної) конкуренції від інших форм організ...Принципові відмінності досконалої (повної) конкуренції від інших форм організ...
Принципові відмінності досконалої (повної) конкуренції від інших форм організ...JurgenstiX
 
Хімічні елементи в літературних творах 8 клас
Хімічні елементи в літературних творах 8 класХімічні елементи в літературних творах 8 клас
Хімічні елементи в літературних творах 8 класkrementsova09nadya
 
Бомбочки для ванни своїми руками презентація
Бомбочки для ванни своїми руками презентаціяБомбочки для ванни своїми руками презентація
Бомбочки для ванни своїми руками презентаціяssuser0a4f48
 
Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»
Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»
Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»tetiana1958
 
Проблеми захисту лісу в Україні та шляхи вирішення
Проблеми захисту лісу в Україні та шляхи вирішенняПроблеми захисту лісу в Україні та шляхи вирішення
Проблеми захисту лісу в Україні та шляхи вирішенняtetiana1958
 

Último (12)

Бібліотека – розвиток дитячої творчості та дозвілля для дітейpptx
Бібліотека – розвиток дитячої творчості  та дозвілля для дітейpptxБібліотека – розвиток дитячої творчості  та дозвілля для дітейpptx
Бібліотека – розвиток дитячої творчості та дозвілля для дітейpptx
 
Р.Шеклі "Запах думки". Аналіз оповідання
Р.Шеклі "Запах думки". Аналіз оповіданняР.Шеклі "Запах думки". Аналіз оповідання
Р.Шеклі "Запах думки". Аналіз оповідання
 
атестація 2023-2024 Kewmrbq wtynh GNJ.pdf
атестація 2023-2024 Kewmrbq wtynh GNJ.pdfатестація 2023-2024 Kewmrbq wtynh GNJ.pdf
атестація 2023-2024 Kewmrbq wtynh GNJ.pdf
 
Презентациія для сайта Група «Незабудка».pptx
Презентациія для сайта Група «Незабудка».pptxПрезентациія для сайта Група «Незабудка».pptx
Презентациія для сайта Група «Незабудка».pptx
 
ЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.ppt
ЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.pptЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.ppt
ЛЕКЦІЯ Засоби масової інформації –важливий інструмент ПР.ppt
 
Іваніщук Надія Вікторівна атестація .pdf
Іваніщук Надія Вікторівна атестація  .pdfІваніщук Надія Вікторівна атестація  .pdf
Іваніщук Надія Вікторівна атестація .pdf
 
Застосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdf
Застосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdfЗастосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdf
Застосування Гайду безбар’єрності в роботі закладів культури громад Одещини.pdf
 
Принципові відмінності досконалої (повної) конкуренції від інших форм організ...
Принципові відмінності досконалої (повної) конкуренції від інших форм організ...Принципові відмінності досконалої (повної) конкуренції від інших форм організ...
Принципові відмінності досконалої (повної) конкуренції від інших форм організ...
 
Хімічні елементи в літературних творах 8 клас
Хімічні елементи в літературних творах 8 класХімічні елементи в літературних творах 8 клас
Хімічні елементи в літературних творах 8 клас
 
Бомбочки для ванни своїми руками презентація
Бомбочки для ванни своїми руками презентаціяБомбочки для ванни своїми руками презентація
Бомбочки для ванни своїми руками презентація
 
Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»
Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»
Відкрита лекція на тему «Контроль бур'янів в посівах соняшника»
 
Проблеми захисту лісу в Україні та шляхи вирішення
Проблеми захисту лісу в Україні та шляхи вирішенняПроблеми захисту лісу в Україні та шляхи вирішення
Проблеми захисту лісу в Україні та шляхи вирішення
 

Курсовая (1).pdf

  • 1. Національний університет кораблебудування імені адмірала Макарова Навчально-науковий інститут комп'ютерних наук та управління проектами КУРСОВИЙ ПРОЕКТ (РОБОТА) з ” Технології розробки програмного забезпечення на сучасних платформах” (назва дисципліни) на тему: Онлайн бібліотека Студента II курсу 2151 групи спеціальності 121 Гурмази.М.В (прізвище та ініціали) Керівник cтарший викладачі, Беркунський Є.Ю, Смикодуб.Т.Г Національна шкала ________________ Кількість балів: __________Оцінка: ECTS _____ Члени комісії ________________ Беркунський Є.Ю (підпис) ________________ Смикодуб.Т.Г (підпис)
  • 2. АНОТАЦІЯ У даній курсовій роботі розглядається проєкт WEB додатку “Онлайн Бібліотека”. Робота виконана на 50 сторінках, містить 8 рисунків. Робота містить 1 програму, з текстом програми та результатами. Робота виконана українською мовою. ABSTRACT This course work represents the WEB application project "Online library)". The work is completed on 50 pages, contains 8 drawings. The work contains 1 program, with program text and results. The work was done in Ukrainian. ЗМІСТ ВСТУП…………………………………………………………………....4 1. АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ …… ………………………………....5 2. ПОСТАНОВКА ЗАДАЧІ……… ….…………………………………………..6 3. ДІАГРАМА СУТНІСТЬ-ЗВ'ЯЗОК…….…………………………………….6 4. ДІАГРАМА КЛАСІВ……..………………………………………………………7 5. ДІАГРАМА ПОСЛІДОВНОСТІ…..…………………………………………8 6. ДІАГРАМА СТАНІВ…..…………………………………………………………9 7. ЗАСОБИ РОЗРОБКИ……………………….…………………………………..10 8. РЕЗУЛЬТАТИ РОЗРОБКИ…………………….……………………………..13 ВИСНОВКИ…………………………………………………………………………....45 СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ…………………………...……..…..46 ДОДАТОК А…………………………………………………………….…...………..47 2
  • 3. ВСТУП При наявності великої кількості користувачів та книг у бібліотеці важливо мати зручні методи обслуговування та ефективний облік доступних книг та користувачів. Методом обслуговування для користувачів може бути можливість читання книг, а адміністраторам необхідно мати можливість редагування та додавання нових книг. Для ведення обліку доступних книг та користувачів ідеально підходять бази даних, де інформація зберігається в зрозумілому та зручному форматі, з можливістю легкої маніпуляції (додавання/видалення записів, редагування даних). Як предметну область я обрав "Онлайн бібліотеку", для якої необхідно створити веб-додаток з можливістю використання бази даних для обліку доступних книг та користувачів. Ця база даних може містити наступну інформацію: назву книги, автора, видавницвто, жанр, рік видання, опис, кількість сторінок, текст. Онлайн бібліотека повинна надавати користувачам можливість перегляду доступних книг, пошуку за різними критеріями, перегляду деталей про книгу та читання її. Адміністратори повинні мати можливість редагування існуючих книг, додавання нових книг.
  • 4. 3 АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ Онлайн бібліотека – це електронний сервіс, який надає користувачам доступ до електронних книг та інших текстових матеріалів через Інтернет. Зазвичай онлайн бібліотеки дозволяють користувачам шукати та переглядати книги. Робота онлайн бібліотеки може бути організована так: користувач реєструється на веб-сайті, після чого отримує доступ до каталогу електронних книг. Користувач може шукати книги за назвою та жанром, вибирати книги для читання. Як адміністратор онлайн бібліотеки може виступати бібліотекар, який керує каталогом електронних книг та забезпечує доступність книг для користувачів. Адміністратор може додавати нові книги до каталогу, змінювати інформацію про книги, видаляти книги. Для забезпечення безпеки та конфіденційності даних користувача онлайн бібліотека може використовувати систему реєстрації та авторизації користувачів, а також захист даних з використанням шифрування. Також може бути реалізована система контролю доступу, яка дозволяє обмежувати доступ до книг залежно від статусу користувача. Таким чином, онлайн бібліотека дозволяє користувачам насолоджуватися читанням книг у зручний час та місце, а адміністратору – забезпечувати доступність та оновлення каталогу електронних книг.
  • 5. 4 ПОСТАНОВКА ЗАДАЧІ Завдання полягає у створенні веб-додатку для надання послуг онлайн бібліотеки. Веб-додаток повинен мати привабливий та легко сприйнятний графічний інтерфейс користувача. Він має включати перелік доступних послуг та можливість користувачам реєструватися та отримувати доступ до електронних книг. Основні функціональні можливості веб-додатку для онлайн бібліотеки включають: 1. Реєстрація користувачів: Користувачі можуть створювати облікові записи в системі, вводячи необхідні дані, такі як ім'я, електронна адреса та пароль. 2. Авторизація та аутентифікація: Користувачі можуть увійти в систему за допомогою свого облікового запису, використовуючи введений при реєстрації пароль. Система повинна забезпечити безпеку даних та обмеження доступу до функцій, що призначені тільки для адміністратора. 3. Перегляд каталогу книг: Користувачі можуть переглядати каталог електронних книг, використовуючи різні фільтри, такі як назва, автор, жанр, рік видання і т.д. Кожна книга повинна мати відповідну сторінку з описом, обкладинкою та можливістю перегляду додаткових деталей. 4. Пошук книг: Користувачі можуть використовувати пошукову функцію для знаходження конкретної книги. 5. Читання книг: Користувачі мають можливість відкривати електронні книги для читання в онлайн-режимі.
  • 6. 5 ДІАГРАМА КЛАСІВ Рисунок 1 — діаграма класів
  • 7. 6 ДІАГРАМА СУТНІСТЬ-ЗВ’ЯЗОК Рисунок 2 – діаграма сутність-зв’язок
  • 8. 7 ДІАГРАМА ПОСЛІДОВНОСТІ Діаграма послідовності для класу BookController.java представлена на рисунку 3.
  • 9. 8 ДІАГРАМА ПОСЛІДОВНОСТІ Діаграма послідовності для класу UserService.java представлена на рисунку 4.
  • 10. ДІАГРАМА CТАНІВ Діаграма станів для класу UserService.java представлена на рисунку 5 9 ДІАГРАМА CТАНІВ Діаграма станів для класу BookController.java представлена на рисунку 4.
  • 11. 10
  • 12. ЗАСОБИ РОЗРОБКИ Для розробки проєкту мною було обране програмне забезпечення JetBrains IntelliJ IDEA є провідним середовищем швидкого розвитку мови Java. IntelliJ IDEA — це високотехнологічний комплекс тісно інтегрованих інструментів програмування, включаючи інтелектуальний редактор джерел з передовими інструментами автоматизації, потужні інструменти рефакторингу коду, вбудовану підтримку технологій J2EE, інтеграційні механізми з середовищем тестування Ant/ JUnit та системами контролю версій, унікальний інструмент оптимізації та перевірки перевірки коду, а також інноваційний візуальний графічний інтерфейс. Унікальні особливості JetBrains IntelliJ IDEA знімають з програміста тягар рутинної роботи, допомагають своєчасно усунути помилки і поліпшити якість коду, піднявши продуктивність розробника на нову висоту. Мовою програмування була обрана Java 17 версії. Java — об’єктноорієнтована мова програмування, випущена 1995 року компанією «Sun Microsystems» як основний компонент платформи Java. З 2009 року мовою займається компанія «Oracle», яка того року придбала «Sun Microsystems». В офіційній реалізації Java-програми компілюються у байт- код, який при виконанні інтерпретується віртуальною машиною для конкретної платформи. «Oracle» надає компілятор Java та віртуальну машину Java, які задовольняють специфікації Java Community Process, під ліцензією GNU General Public License. Об'єктно–орієнтоване програмування (ООП) — це модель програмування яка базується на стверджені того, що програма це сукупність об’єктів які взаємодіють між собою. Кожен об’єкт в цій моделі є незалежним, і він здатний отримувати, обробляти дані та відправляти ці дані іншим об’єктам. В ООП використано моделі успадкування, модульності, поліморфізму та інкапсуляції. Maven — це інструмент побудови та управління проектами, який зазвичай використовується в фреймворках, побудованих на Java. Він розроблений Apache Software Foundation. Maven, слово з мови ідиш, означає «збирач знань». Він був введений, щоб зробити процес запуску побудови в Джакартському турбінному проекті. 11
  • 13. Maven контролюється файлом Project Object Model (pom). Під час роботи з вбудованими фреймворками Java нам часто доводиться мати справу з низкою залежностей. До того, як Maven з'явився в картині, усі залежності, які є не чим іншим, як файлами JAR, повинні були бути додані в наш фреймворк вручну. Крім того, нам потрібно було подбати про оновлення програмного забезпечення в нашому проекті. HTML (Hypertext Markup Language — Мова гіпертекстової розмітки) — це мова опису структури сторінок документів, яка дозволяє звичайний текст форматувати в абзаци, заголовки, списки та інші структури, створювати посилання на інші сторінки. Це текстова мова, в якій інструкції з форматування, що називаються тегами, вбудовані в розділи документа, які містять конкретну інформацію. Теги повідомляють браузерам, як форматувати і представляти інформацію на екрані. CSS (абревіатура від Cascading Style Sheets, що в перекладі означає каскадні таблиці стилів) — це спеціальна мова (мова стилів), за допомогою якої описують вигляд документів (як і де відображати елементи вебсторінки), написаних мовами розмітки даних. Найчастіше CSS використовується для документів, котрі розмічені мовою HTML, XHTML та XML. Lombok — це плагін компілятора, який додає в Java нові ключові слова і перетворює анотації в Java-код, зменшуючи зусилля на розробку і забезпечуючи деяку додаткову функціональність. Spring MVC забезпечує архітектуру патерна Model - View - Controller (Модель - Відображення - Контролер) — структура для створення слабо пов'язаних веб-додатків, що розділяє основні аспекти їх розробки: об'єкти, бізнес-логіку та зовнішній вигляд програми. Основна перевага архітектури MVC — можливість міняти один із компонентів програми, суттєво не впливаючи на інші. Spring Security - це популярний модуль у Spring Framework, який забезпечує аутентифікацію і авторизацію веб-додатків. Він додає безпеку на рівні URL, методу або навіть на рівні доменних об'єктів. 12
  • 14. MariaDB — відгалуження реляційної СУБД MySQL, що розробляється спільнотою під ліцензією GPL. MariaDB повністю сумісна з програмами, що використовують MySQL, а перехід на цю СУБД виправданий тим, що MySQL вже не так активно розвивається. У MariaDB вбудовані покращений оптимізатор запитів, безпечна та швидка реплікація, швидші індекси для механізму зберігання даних MEMORY(HEAP), більш висока продуктивність перекодування символів, використання пулу потоків, а також багато інших покращень, що позитивно впливають на продуктивність. Результати Розробки Результати розробки загалом представлені на лістингах 1-20. Javaкласи представлені на лістингах 1-11. Сторінки HTML на лістингах 12-18. Сторінки css на лістингах 19-20. Лістинг 1 OnLibApplication.java package com.example.OnLib; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class OnLibApplication { public static void main(String[] args) { SpringApplication.run(OnLibApplication.class, args); } } Лістинг 2 SecurityConfig.java package com.example.OnLib.configurations; import com.example.OnLib.services.CustomUserDetailsService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.Authen ticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlo balMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSec urity; 13
  • 15. import org.springframework.security.config.annotation.web.configuration.WebSecurityC onfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @EnableWebSecurity @RequiredArgsConstructor @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { private final CustomUserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/read", "/books-by-title") .authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(8); } } Лістинг 3 BookController.java package com.example.OnLib.controllers; import com.example.OnLib.dao.BookRepository; import com.example.OnLib.entities.Book; import lombok.AllArgsConstructor; import org.springframework.http.*; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; 14
  • 16. import org.springframework.web.server.ResponseStatusException; import java.io.IOException; import java.util.List; import java.util.Optional; @Controller @AllArgsConstructor public class BookController { private BookRepository bookRepository; @GetMapping("/") public String showAllBooks(Model model) { List<Book> books = bookRepository.findAll(); model.addAttribute("books", books); return "mainPage"; } @PostMapping("/books-by-title") public String showBooksByAuthor(@RequestParam("title") String title, Model model) { List<Book> books = bookRepository.findBookByTitle(title); model.addAttribute("books", books); model.addAttribute("title", title); return "index"; } @PostMapping("/addBooks") public String addBooks(@RequestParam String title, @RequestParam String author, @RequestParam String publisher, @RequestParam String genre, @RequestParam String description, @RequestParam int number_Of_Pages, @RequestParam int year_Of_Publication, @RequestParam MultipartFile image, @RequestParam String text) { Book book = new Book(); book.setTitle(title); book.setAuthor(author); book.setPublisher(publisher); book.setGenre(genre); book.setDescription(description); book.setNumberOfPages(number_Of_Pages); book.setYearOfPublication(year_Of_Publication); book.setText(text); try { book.setImage(image.getBytes()); } catch (IOException e) { e.printStackTrace(); } bookRepository.save(book); return "redirect:/"; } @GetMapping("/deleteBook") public String deleteBook (@RequestParam int id){ bookRepository.deleteById(id); return "redirect:/"; } @GetMapping("/editBook") public String editBook(@RequestParam int id, Model model){ Optional <Book> optionalBook = bookRepository.findById(id); if(optionalBook.isEmpty()){ return "redirect:/"; 15
  • 17. } model.addAttribute("book", optionalBook.get()); return "edit"; } @PostMapping("/updateBook") public String updateBook (@RequestParam int id, @RequestParam String title, @RequestParam String author, @RequestParam String publisher, @RequestParam String genre, @RequestParam String description, @RequestParam int number_Of_Pages, @RequestParam int year_Of_Publication, @RequestParam MultipartFile image, @RequestParam String text) { Optional<Book> optionalBook = bookRepository.findById(id); optionalBook.ifPresent(t -> { t.setTitle(title); t.setAuthor(author); t.setPublisher(publisher); t.setGenre(genre); t.setDescription(description); t.setNumberOfPages(number_Of_Pages); t.setYearOfPublication(year_Of_Publication); t.setText(text); try { t.setImage(image.getBytes()); } catch (IOException e) { throw new RuntimeException(e); } bookRepository.save(t); }); return "redirect:/"; } @GetMapping("/book/{id}/image") public ResponseEntity<byte[]> getImage(@PathVariable Integer id) { Book book = bookRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Book not found")); byte[] image = book.getImage(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.IMAGE_JPEG); return new ResponseEntity<>(image, headers, HttpStatus.OK); } @GetMapping("/read") public String showBookText(Model model, @RequestParam("id") Integer bookId) { Book book = bookRepository.findById(bookId).orElse(null); model.addAttribute("bookText", book.getText()); return "readBook"; } @GetMapping("/romance-books") public String showRomanceBooks(Model model) { List<Book> books = bookRepository.findByGenre("Роман"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/detective-books") public String showDetectiveBooks(Model model) { List<Book> books = bookRepository.findByGenre("Детектив"); model.addAttribute("books", books); return "secondPage"; 16
  • 18. } @GetMapping("/drama-books") public String showDramaBooks(Model model) { List<Book> books = bookRepository.findByGenre("Драма"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/fantasy-books") public String showFentBooks(Model model) { List<Book> books = bookRepository.findByGenre("Фэнтези"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/triller-books") public String showTrillerBooks(Model model) { List<Book> books = bookRepository.findByGenre("Триллер/Ужасы"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/adventures-books") public String showAdventureBooks(Model model) { List<Book> books = bookRepository.findByGenre("Приключения"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/psyh-books") public String showPsyhBooks(Model model) { List<Book> books = bookRepository.findByGenre("Психология"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/fairy-books") public String showFairyBooks(Model model) { List<Book> books = bookRepository.findByGenre("Сказки"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/classic-books") public String showClassicBooks(Model model) { List<Book> books = bookRepository.findByGenre("Классика"); model.addAttribute("books", books); return "secondPage"; } @GetMapping("/mistik-books") public String showMistikBooks(Model model) { List<Book> books = bookRepository.findByGenre("Мистика"); model.addAttribute("books", books); return "secondPage"; } } 17
  • 19. Лістинг 4 UserController.java package com.example.OnLib.controllers; import com.example.OnLib.entities.User; import com.example.OnLib.services.UserService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @Controller @RequiredArgsConstructor public class UserController { private final UserService userService; @GetMapping("/login") public String login() { return "login"; } @GetMapping("/mainPage") public String mainPage() { return "mainPage"; } @GetMapping("/logout") public String logout(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { session.invalidate(); } return "redirect:/login?logout"; } @GetMapping("/registration") public String registration(Model model) { model.addAttribute("user", new User()); return "registration"; } @PostMapping("/registration") public String createUser(User user, Model model) { if (!userService.createUser(user)) { model.addAttribute("errorMessage", "Пользователь с email: " + user.getEmail() + " уже существует"); return "registration"; } return "redirect:/login"; } 18
  • 20. Лістинг 5 BookReoisitory.java package com.example.OnLib.dao; import com.example.OnLib.entities.Book; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface BookRepository extends JpaRepository<Book, Integer> { List<Book> findBookByTitle(String title); List<Book> findByGenre(String genre); } Лістинг 6 UserRepoisitory.java package com.example.OnLib.dao; import com.example.OnLib.entities.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, Long> { User findByEmail(String email); } Лістинг 7 Role.java package com.example.OnLib.entities.enums; import org.springframework.security.core.GrantedAuthority; public enum Role implements GrantedAuthority { ROLE_USER, ROLE_ADMIN; @Override public String getAuthority() { return name(); } } 19
  • 21. Лістинг 8 Book.java package com.example.OnLib.entities; import lombok.Getter; import lombok.Setter; import javax.persistence.*; @Getter @Setter @Entity @Table(name = "book") public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Integer id; @Column(name = "title", length = 50) private String title; @Column(name = "author", length = 50) private String author; @Column(name = "publisher", length = 50) private String publisher; @Column(name = "yearOfPublication") private Integer yearOfPublication; @Column(name = "numberOfPages") private Integer numberOfPages; @Column(name = "image") private byte[] image; @Column(name = "genre") private String genre; @Column(name = "description") private String description; @Column(name = "text") private String text; } Лістинг 9 User.java package com.example.OnLib.entities; import com.example.OnLib.entities.enums.Role; import lombok.Data; 20
  • 22. import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import javax.persistence.*; import java.time.LocalDateTime; import java.util.*; @Entity @Table(name = "users") @Data public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "email", unique = true) private String email; @Column(name = "active") private boolean active; @Column(name = "password", length = 1000) private String password; @ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER) @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id")) @Enumerated(EnumType.STRING) private Set<Role> roles = new HashSet<>(); private LocalDateTime dateOfCreated; @PrePersist private void init() { dateOfCreated = LocalDateTime.now(); } // security public boolean isAdmin() { return roles.contains(Role.ROLE_ADMIN); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return roles; } @Override public String getUsername() { 21
  • 23. return email; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return active; } } Лістинг 10 CustomUserDetailsService.java package com.example.OnLib.services; import com.example.OnLib.dao.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { return userRepository.findByEmail(email); }} 22
  • 24. Лістинг 11 UserService.java package com.example.OnLib.services; import com.example.OnLib.entities.User; import com.example.OnLib.entities.enums.Role; import com.example.OnLib.dao.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; @Service @Slf4j @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; public boolean createUser(User user) { String email = user.getEmail(); if (userRepository.findByEmail(email) != null) return false; user.setActive(true); user.setPassword(passwordEncoder.encode(user.getPassword())); user.getRoles().add(Role.ROLE_USER); userRepository.save(user); return true; } public List<User> list() { return userRepository.findAll(); } } Лістинг 12 mainPage.html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <head> <title>OnLib</title> <link th:rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}"> <link th:rel="stylesheet" type="text/css" th:href="style.css"> </head> <body> <header> <nav class="navbar navbar-expand-lg navbar-light bg-light"> 23
  • 25. <div class="container-fluid"> <a class="navbar-brand" th:href="@{/}">OnLib</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria- label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" th:href="@{/}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Books</a> </li> </ul> <ul class="navbar-nav"> <div th:if="${#httpServletRequest.userPrincipal != null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/logout}">Logout</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/registration}">Register</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/login}">Login</a> </li> </div> </ul> </div> </div> </nav> </header> <form method="post" th:action="@{/books-by-title}"> <input type="text" id="titless" name="title" class="form-control" placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2" required> <button type="submit" class="btn btn-outline-dark " >Искать</button> </form> <button type="button" class="btn btn btn-outline-danger" data-bs- toggle="modal" data-bs-target="#exampleModal" data-bs- whatever="@getbootstrap" sec:authorize="hasRole('ROLE_ADMIN')" >Добавить книгу</button> <div class="modal fade" id="exampleModal" tabindex="-1" aria- labelledby="exampleModalLabels" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="exampleModalLabels" >Добавить книгу</h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria- label="Close"></button> 24
  • 26. <form th:action="@{/addBooks}" method="post" enctype="multipart/form- data"> <input type="text" id="titles" name="title" placeholder="Название" aria-label="Название"/> <input type="text" id="authors" name="author" placeholder="Автор" aria-label="Автор"/> <input type="text" id="publishers" name="publisher" placeholder="Издательство" aria-label="Издательство"/> <input type="text" id="genres" name="genre" placeholder="Жанр" aria-label="Жанр"/> <input type="text" id="descriptions" name="description" placeholder="Описание" aria-label="Описание"/> <input type="number" id="year_Of_Publications" name="year_Of_Publication" placeholder="Год" aria-label="Год"/> <input type="number" id="number_Of_Pagess" name="number_Of_Pages" placeholder="Количество страниц" aria-label="Количество страниц"/> <input type="file" id="images" name="image" accept="image/*" required/> <textarea id="text" name="text" placeholder="Текст" aria- label="Текст"></textarea> <button type="submit">Добавить</button> </form> <button type="button" class="btn btn-secondary" data-bs- dismiss="modal">Закрыть</button> </div> </div> </div> </div> <div class="tab"> <table id="table" class="table table-striped table-sm table-hover"> <tbody> <tr th:each="book : ${books} "> <td> <img th:src="@{/book/{id}/image(id=${book.id})}" class="img-fluid" width="220" height="220" alt="Book Cover"> <p> <span>Название: </span><span th:text="${book.title}"></span><br> <span>Автор: </span><span th:text="${book.author}"></span><br> <span>Издательство: </span><span th:text="${book.publisher}"></span><br> <span>Жанр: </span><span th:text="${book.genre}"></span><br> <span>Год: </span><span th:text="${book.yearOfPublication }"></span><br> <span>Количество страниц: </span><span th:text="${book.numberOfPages}"></span><br> <a th:href="@{/read(id=${book.id})}" target="_blank" class="btn btn-primary">Читать онлайн</a> <a class="btn btn-danger" data-bs-toggle="modal" data-bs- target="#myconfirm" th:attr="data-bs-link=@{/deleteBook(id=${book.id})}, data-bs- text=${book.title}, data-bs-id=${book.id}" sec:authorize="hasRole('ROLE_ADMIN')">Удалить</a> <a th:href="@{/editBook(id=${book.id})}" class="btn btn-success" style="margin-bottom: 30px" sec:authorize="hasRole('ROLE_ADMIN')" >Редактировать</a> <div class="opys"><span>Описание: </span><span th:text="${book.description}"></span>></div><br> 25
  • 27. </p> </td> </tr> </tbody> </table> </div> <div class="modal fade" id="myconfirm" data-bs-backdrop="static" data-bs- keyboard="false" tabindex="-1" aria-labelledby="staticWarningLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="staticWarningLabel">Warning</h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria- label="Close"></button> </div> <div class="modal-body"> Вы уверены что хотите удалить книгу?<br/> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs- dismiss="modal"> Отменить </button> <a class="btn btn-danger delete-button" style="margin-top: 30px">Удалить</a> </div> </div> </div> </div> <script> var myConfirmModal = document.getElementById('myconfirm') myConfirmModal.addEventListener('show.bs.modal', function (event) { var button = event.relatedTarget var link = button.getAttribute('data-bs-link') var tid = button.getAttribute('data-bs-id') var text = button.getAttribute('data-bs-text') var modalTitle = myConfirmModal.querySelector('.modal-title') modalTitle.textContent = 'Удалить книгу: ' + text + '(' + tid + ')' var deleteButton = myConfirmModal.querySelector('.delete-button') deleteButton.setAttribute("href", link) }) </script> <div style="width:140px;"> <td class="left" align="left" valign="top" width="202px"> <ul id="menu_left"> <li><a th:href="@{/romance-books}" title="Роман">Роман</a></li> <li><a th:href="@{/detective-books}" title="Детективы">Детективы</a></li> <li><a th:href="@{/fantasy-books}" title="Фэнтези">Фэнтези</a></li> <li><a th:href="@{/drama-books}" title="Драма">Драма</a></li> <li><a th:href="@{triller-books}" title="Триллер/Ужасы">Триллер/Ужасы</a></li> <li><a th:href="@{/adventures-books}" title="Приключения">Приключения</a></li> <li><a th:href="@{/psyh-books}" title="Психология">Психология</a></li> <li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li> <li><a th:href="@{/classic-books}" title="Классика">Классика</a></li> 26
  • 28. <li><a th:href="@{/mistik-books}" title="Мистика">Мистика</a></li> </ul> </td> </div> <script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script> <script th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script> </body> </html> Лістинг 13 login.html <!DOCTYPE html> <html> <head> <meta charset="ISO-8859-1"> <link th:rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384- BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <style> body{ background-color: #e2f1ff; } </style> <header> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" >OnLib</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria- label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" th:href="@{/}">Home</a> </li> </ul> 27
  • 29. </div> </div> </nav> </header> <br> <br> <div class = "container"> <div class = "row"> <div class = "col-md-6 col-md-offset-3"> <h1> User Login Page </h1> <form th:action="@{/login}" method="post"> <!-- error message --> <div th:if="${param.error}"> <div class="alert alert-danger">Invalid username or password.</div> </div> <!-- logout message --> <div th:href="@{param.logout}"> <div class="alert alert-info" style="background-color: white">You have been logged out.</div> </div> <div class = "form-group"> <label for ="username"> Username </label> : <input type="text" class = "form-control" id ="username" name = "username" placeholder="Enter Email ID" autofocus="autofocus"> </div> <div class="form-group"> <label for="password">Password</label>: <input type="password" id="password" name="password" class="form-control" placeholder="Enter Password" /> </div> <div class="form-group"> <div class="row"> <div class="col-sm-6 col-sm-offset-3"> <input type="submit" name="login-submit" id="login-submit" class="form-control btn btn-primary" value="Log In" /> </div> </div> </div> </form> <div class="form-group"> <span>New user? <a href="/" th:href="@{/registration}">Register here</a></span> </div> </div> </div> </div> </body> </html> 28
  • 30. Лістинг 14 index.html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <head> <meta charset="UTF-8"> <title>OnLib</title> <link th:rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}"> <link th:rel="stylesheet" type="text/css" th:href="style.css"></head> <body> <header> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" href="/index">OnLib</a> <button class="navbar-toggler" type="button" data-bs- toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria- label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" th:href="@{/}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Books</a> </li> </ul> <ul class="navbar-nav"> <div th:if="${#httpServletRequest.userPrincipal != null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/logout}">Logout</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/registration}">Register</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/login}">Login</a> </li> </div> </ul> </div> </div> 29
  • 31. </nav> </header> <form method="post" th:action="@{/books-by-title}"> <input type="text" id="titles" name="title" class="form-control" placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2" required> <button type="submit" class="btn btn-outline-dark " >Искать</button> </form> <button type="button" class="btn btn btn-outline-danger" data-bs- toggle="modal" data-bs-target="#exampleModal" data-bs- whatever="@getbootstrap" sec:authorize="hasRole('ROLE_ADMIN')" >Добавить книгу</button> <div class="modal fade" id="exampleModal" tabindex="-1" aria- labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="exampleModalLabel" >Добавить книгу</h1> <button type="button" class="btn-close" data-bs- dismiss="modal" aria-label="Close"></button> <form th:action="@{/addBooks}" method="post" enctype="multipart/form-data"> <input type="text" id="title" name="title" placeholder="Название" aria-label="Название"/> <input type="text" id="author" name="author" placeholder="Автор" aria-label="Автор"/> <input type="text" id="publisher" name="publisher" placeholder="Издательство" aria-label="Издательство"/> <input type="text" id="genre" name="genre" placeholder="Жанр" aria-label="Жанр"/> <input type="text" id="description" name="description" placeholder="Описание" aria-label="Описание"/> <input type="number" id="year_Of_Publication" name="year_Of_Publication" placeholder="Год" aria-label="Год"/> <input type="number" id="number_Of_Pages" name="number_Of_Pages" placeholder="Количество страниц" aria- label="Количество страниц"/> <input type="file" id="image" name="image" accept="image/*" required/> <textarea id="text" name="text" placeholder="Текст" aria- label="Текст"></textarea> <button type="submit">Add Book</button> </form> <button type="button" class="btn btn-secondary" data-bs- dismiss="modal">Close</button> </div> </div> </div> </div> <div class="tab"> <table id="table" class="table table-striped table-sm table-hover"> <tbody> <tr th:each="book : ${books} "> <td> <img th:src="@{/book/{id}/image(id=${book.id})}" class="img- fluid" width="220" height="220" alt="Book Cover"> <p> <span>Название: </span><span 30
  • 32. th:text="${book.title}"></span><br> <span>Автор: </span><span th:text="${book.author}"></span><br> <span>Издательство: </span><span th:text="${book.publisher}"></span><br> <span>Жанр: </span><span th:text="${book.genre}"></span><br> <span>Год: </span><span th:text="${book.yearOfPublication }"></span><br> <span>Количество страниц: </span><span th:text="${book.numberOfPages}"></span><br> <a th:href="@{/read(id=${book.id})}" target="_blank" class="btn btn-primary">Читать онлайн</a> <a class="btn btn-danger" data-bs-toggle="modal" data-bs- target="#myconfirm" th:attr="data-bs-link=@{/deleteBook(id=${book.id})}, data-bs-text=${book.title}, data-bs-id=${book.id}" sec:authorize="hasRole('ROLE_ADMIN')">Удалить</a> <a th:href="@{/editBook(id=${book.id})}" class="btn btn- success" style="margin-bottom: 30px" sec:authorize="hasRole('ROLE_ADMIN')" >Редактировать</a> <div class="opys"><span>Описание: </span><span th:text="${book.description}"></span>></div><br> </p> </td> </tr> </tbody> </table> </div> <div class="modal fade" id="myconfirm" data-bs-backdrop="static" data-bs- keyboard="false" tabindex="-1" aria-labelledby="staticWarningLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="staticWarningLabel">Warning</h1> <button type="button" class="btn-close" data-bs- dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> Вы уверены что хотите удалить книгу?<br/> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs- dismiss="modal"> Отменить </button> <a class="btn btn-danger delete-button" style="margin-top: 30px">Удалить</a> </div> </div> </div> </div> <script> var myConfirmModal = document.getElementById('myconfirm') myConfirmModal.addEventListener('show.bs.modal', function (event) { var button = event.relatedTarget 31
  • 33. var link = button.getAttribute('data-bs-link') var tid = button.getAttribute('data-bs-id') var text = button.getAttribute('data-bs-text') var modalTitle = myConfirmModal.querySelector('.modal-title') modalTitle.textContent = 'Удалить книгу: ' + text + '(' + tid + ')' var deleteButton = myConfirmModal.querySelector('.delete-button') deleteButton.setAttribute("href", link) }) </script> <div style="width:140px;"> <td class="left" align="left" valign="top" width="202px"> <ul id="menu_left"> <li><a th:href="@{/romance-books}" title="Роман">Роман</a></li> <li><a th:href="@{/detective-books}" title="Детективы">Детективы</a></li> <li><a th:href="@{/fantasy-books}" title="Фэнтези">Фэнтези</a></li> <li><a th:href="@{/drama-books}" title="Драма">Драма</a></li> <li><a th:href="@{triller-books}" title="Триллер/Ужасы">Триллер/Ужасы</a></li> <li><a th:href="@{/adventures-books}" title="Приключения">Приключения</a></li> <li><a th:href="@{/psyh-books}" title="Психология">Психология</a></li> <li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li> <li><a th:href="@{/classic-books}" title="Классика">Классика</a></li> <li><a th:href="@{/mistik-books}" title="Мистика">Мистика</a></li> </ul> </td> </div> <script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script> <script th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script> </body> </html> Лістинг 15 edit.html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>OnLib</title> <link th:rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}"> <link th:rel="stylesheet" type="text/css" th:href="edit.css"> </head> <body> <header> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" th:href="@{/}">OnLib</a> 32
  • 34. <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria- label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" th:href="@{/}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Books</a> </li> </ul> <ul class="navbar-nav"> <div th:if="${#httpServletRequest.userPrincipal != null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/logout}">Logout</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/registration}">Register</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/login}">Login</a> </li> </div> </ul> </div> </div> </nav> </header> <form th:action="@{/updateBook}" method="post" enctype="multipart/form-data"> <input type="hidden" id="id" name="id" th:value="${book.id}" /> <input type="text" id="title" name="title" placeholder="Название" aria- label="Название" th:value="${book.title}" /> <input type="text" id="author" name="author" placeholder="Автор" aria- label="Автор" th:value="${book.author}" /> <input type="text" id="publisher" name="publisher" placeholder="Издательство" aria-label="Издательство" th:value="${book.publisher}" /> <input type="text" id="genre" name="genre" placeholder="Жанр" aria- label="Жанр" th:value="${book.genre}" /> <input type="text" id="description" name="description" placeholder="Описание" aria-label="Описание" th:value="${book.description}" /> <input type="number" id="year_Of_Publication" name="year_Of_Publication" placeholder="Год" aria-label="Год" th:value="${book.yearOfPublication}" /> <input type="number" id="number_Of_Pages" name="number_Of_Pages" placeholder="Количество страниц" aria-label="Количество страниц" th:value="${book.numberOfPages}" /> <input type="file" id="image" name="image" accept="image/*" /> <textarea id="text" name="text" placeholder="Текст" aria-label="Текст" th:text="${book.text}"></textarea> <button type="submit">Изменить</button> </form> </body> 33
  • 35. <script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script> <script th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script> </html> Лістинг 16 readBook.html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <head> <title>OnLib</title> <link th:rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}"> <link th:rel="stylesheet" type="text/css" th:href="style.css"></head> <body> <header> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" href="/index">OnLib</a> <button class="navbar-toggler" type="button" data-bs- toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria- label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" th:href="@{/}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Books</a> </li> </ul> <ul class="navbar-nav"> <div th:if="${#httpServletRequest.userPrincipal != null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/logout}">Logout</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/registration}">Register</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/login}">Login</a> 34
  • 36. </li> </div> </ul> </div> </div> </nav> </header> <form method="post" th:action="@{/books-by-title}"> <input type="text" id="title" name="title" class="form-control" placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2" required> <button type="submit" class="btn btn-outline-dark " >Искать</button> </form> <div class="tab"> <p th:text="${bookText}"></p> </div> <div style="width:140px;"> <td class="left" align="left" valign="top" width="202px"> <ul id="menu_left"> <li><a th:href="@{/romance-books}" title="Роман">Роман</a></li> <li><a th:href="@{/detective-books}" title="Детективы">Детективы</a></li> <li><a th:href="@{/fantasy-books}" title="Фэнтези">Фэнтези</a></li> <li><a th:href="@{/drama-books}" title="Драма">Драма</a></li> <li><a th:href="@{triller-books}" title="Триллер/Ужасы">Триллер/Ужасы</a></li> <li><a th:href="@{/adventures-books}" title="Приключения">Приключения</a></li> <li><a th:href="@{/psyh-books}" title="Психология">Психология</a></li> <li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li> <li><a th:href="@{/classic-books}" title="Классика">Классика</a></li> <li><a th:href="@{/mistik-books}" title="Мистика">Мистика</a></li> </ul> </td> </div> <script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script> <script th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script> </body> </html> Лістинг 17 readBook.html <!DOCTYPE html> <html> <head> <link th:rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}"> 35
  • 37. <meta charset="ISO-8859-1"> <title>Registration and Login App</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384- BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <style> body{ background-color: #e2f1ff; } </style> <header> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" >OnLib</a> <button class="navbar-toggler" type="button" data-bs- toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria- label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" th:href="@{/}">Home</a> </li> </ul> </div> </div> </nav> </header> <br> <br> <div class = "container"> <div class = "row"> <div class = "col-md-6 col-md-offset-3"> <h1> User Registration Page </h1> <form th:action="@{/registration}" th:object="${user}" method="post"> <!-- error message --> <div th:if="${errorMessage}"> <div class="alert alert-danger" th:text="${errorMessage}"></div> </div> <div class = "form-group"> <label for ="email"> Email </label> : <input type="email" class = "form-control" id ="email" name = "email" placeholder="Enter Email" autofocus="autofocus" th:field="*{email}"> </div> <div class="form-group"> 36
  • 38. <label for="password">Password</label>: <input type="password" id="password" name="password" class="form-control" placeholder="Enter Password" th:field="*{password}"/> </div> <div class="form-group"> <div class="row"> <div class="col-sm-6 col-sm-offset-3"> <input type="submit" name="register-submit" id="register-submit" class="form-control btn btn-primary" value="Register" /> </div> </div> </div> </form> <div class="form-group"> <span>Already registered? <a href="/login" th:href="@{/login}">Log in here</a></span> </div> </div> </div> </div> </body> </html> Лістинг 18 readBook.html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <head> <title>OnLib</title> <link th:rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css}"> <link th:rel="stylesheet" type="text/css" th:href="style.css"></head> <body> <header> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container-fluid"> <a class="navbar-brand" href="/index">OnLib</a> <button class="navbar-toggler" type="button" data-bs- toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria- label="Toggle navigation" > <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> 37
  • 39. <li class="nav-item"> <a class="nav-link" th:href="@{/}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Books</a> </li> </ul> <ul class="navbar-nav"> <div th:if="${#httpServletRequest.userPrincipal != null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/logout}">Logout</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/registration}">Register</a> </li> </div> <div th:if="${#httpServletRequest.userPrincipal == null}"> <li class="nav-item"> <a class="nav-link" th:href="@{/login}">Login</a> </li> </div> </ul> </div> </div> </nav> </header> <form method="post" th:action="@{/books-by-title}"> <input type="text" id="titles" name="title" class="form-control" placeholder="Поиск..." aria-label="Поиск..." aria-describedby="button-addon2" required> <button type="submit" class="btn btn-outline-dark " >Искать</button> </form> <button type="button" class="btn btn btn-outline-danger" data-bs- toggle="modal" data-bs-target="#exampleModal" data-bs- whatever="@getbootstrap" sec:authorize="hasRole('ROLE_ADMIN')" >Добавить книгу</button> <div class="modal fade" id="exampleModal" tabindex="-1" aria- labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="exampleModalLabel" >Добавить книгу</h1> <button type="button" class="btn-close" data-bs- dismiss="modal" aria-label="Close"></button> <form th:action="@{/addBooks}" method="post" enctype="multipart/form-data"> <input type="text" id="title" name="title" placeholder="Название" aria-label="Название"/> <input type="text" id="author" name="author" placeholder="Автор" aria-label="Автор"/> <input type="text" id="publisher" name="publisher" placeholder="Издательство" aria-label="Издательство"/> 38
  • 40. <input type="text" id="genre" name="genre" placeholder="Жанр" aria-label="Жанр"/> <input type="text" id="description" name="description" placeholder="Описание" aria-label="Описание"/> <input type="number" id="year_Of_Publication" name="year_Of_Publication" placeholder="Год" aria-label="Год"/> <input type="number" id="number_Of_Pages" name="number_Of_Pages" placeholder="Количество страниц" aria- label="Количество страниц"/> <input type="file" id="image" name="image" accept="image/*" required/> <textarea id="text" name="text" placeholder="Текст" aria- label="Текст"></textarea> <button type="submit">Add Book</button> </form> <button type="button" class="btn btn-secondary" data-bs- dismiss="modal">Close</button> </div> </div> </div> </div> <div class="tab"> <table id="table" class="table table-striped table-sm table-hover"> <tbody> <tr th:each="book : ${books} "> <td> <img th:src="@{/book/{id}/image(id=${book.id})}" class="img- fluid" width="220" height="220" alt="Book Cover"> <p> <span>Название: </span><span th:text="${book.title}"></span><br> <span>Автор: </span><span th:text="${book.author}"></span><br> <span>Издательство: </span><span th:text="${book.publisher}"></span><br> <span>Жанр: </span><span th:text="${book.genre}"></span><br> <span>Год: </span><span th:text="${book.yearOfPublication }"></span><br> <span>Количество страниц: </span><span th:text="${book.numberOfPages}"></span><br> <a th:href="@{/read(id=${book.id})}" target="_blank" class="btn btn-primary">Читать онлайн</a> <a class="btn btn-danger" data-bs-toggle="modal" data-bs- target="#myconfirm" th:attr="data-bs-link=@{/deleteBook(id=${book.id})}, data-bs-text=${book.title}, data-bs-id=${book.id}" sec:authorize="hasRole('ROLE_ADMIN')">Удалить</a> <a th:href="@{/editBook(id=${book.id})}" class="btn btn- success" style="margin-bottom: 30px" sec:authorize="hasRole('ROLE_ADMIN')" >Редактировать</a> <div class="opys"><span>Описание: </span><span th:text="${book.description}"></span>></div><br> </p> </td> </tr> </tbody> </table> </div> <div class="modal fade" id="myconfirm" data-bs-backdrop="static" data-bs- keyboard="false" 39
  • 41. tabindex="-1" aria-labelledby="staticWarningLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h1 class="modal-title fs-5" id="staticWarningLabel">Warning</h1> <button type="button" class="btn-close" data-bs- dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> Вы уверены что хотите удалить книгу?<br/> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs- dismiss="modal"> Отменить </button> <a class="btn btn-danger delete-button" style="margin-top: 30px">Удалить</a> </div> </div> </div> </div> <script> var myConfirmModal = document.getElementById('myconfirm') myConfirmModal.addEventListener('show.bs.modal', function (event) { var button = event.relatedTarget var link = button.getAttribute('data-bs-link') var tid = button.getAttribute('data-bs-id') var text = button.getAttribute('data-bs-text') var modalTitle = myConfirmModal.querySelector('.modal-title') modalTitle.textContent = 'Удалить книгу: ' + text + '(' + tid + ')' var deleteButton = myConfirmModal.querySelector('.delete-button') deleteButton.setAttribute("href", link) }) </script> <div style="width:140px;"> <td class="left" align="left" valign="top" width="202px"> <ul id="menu_left"> <li><a th:href="@{/romance-books}" title="Роман">Роман</a></li> <li><a th:href="@{/detective-books}" title="Детективы">Детективы</a></li> <li><a th:href="@{/fantasy-books}" title="Фэнтези">Фэнтези</a></li> <li><a th:href="@{/drama-books}" title="Драма">Драма</a></li> <li><a th:href="@{triller-books}" title="Триллер/Ужасы">Триллер/Ужасы</a></li> <li><a th:href="@{/adventures-books}" title="Приключения">Приключения</a></li> <li><a th:href="@{/psyh-books}" title="Психология">Психология</a></li> <li><a th:href="@{/fairy-books}" title="Сказки">Сказки</a></li> <li><a th:href="@{/classic-books}" title="Классика">Классика</a></li> <li><a th:href="@{/mistik-books}" title="Мистика">Мистика</a></li> </ul> </td> </div> 40
  • 42. <script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script> <script th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script> </body> </html> Лістинг 19 style.css body{ background-color: #e2f1ff; } .form-control, .btn-outline-dark{ width: 300px; margin-left: 300px; margin-top: 10px; } .tab { width: 1000px; position: absolute; height: 300px; left: 50%; transform: translate(-50%, -50%); display: inline-block; margin-top: 250px; } .tab p { display: inline-block; width: calc(100% - 270px); vertical-align: top; } .btn-primary{ margin-bottom: 40px; margin-top: 10px; background-color: #1259a2; } .btn-outline-danger{ margin-top: 50px; margin-left: 670px; } .btn-danger{ margin-bottom: 30px ; } #menu_left { list-style: none; margin: 0; padding: 0; position: fixed; top: 50%; left: 0; transform: translateY(-50%); display: inline-block; 41
  • 43. background-color: #d4e1ff; border: 1px solid ; } #menu_left li { margin-bottom: 10px; } #menu_left li a { display: block; padding: 5px 10px; color: #333; text-decoration: none; border-radius: 5px; } #menu_left li a:hover { background-color: #ccc; } .modal-header { display: flex; flex-direction: column; align-items: center; } .modal-header h1 { margin: 0 0 20px 0; } .modal-header form { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; } .modal-header form input[type="text"], .modal-header form input[type="number"], .modal-header form textarea { margin: 10px; padding: 10px; border: 1px solid #ccc; border-radius: 5px; width: 300px; font-size: 16px; } .modal-header form input[type="file"] { margin: 10px; padding: 10px; font-size: 16px; } .modal-header form button[type="submit"], .modal-header button[type="button"] { margin: 10px; padding: 10px 20px; border-radius: 5px; font-size: 16px; cursor: pointer; 42
  • 44. } .modal-header form button[type="submit"] { background-color: #000000; color: #fff; border: none; } .modal-header button[type="button"] { background-color: #fff; color: #007bff; border: 1px solid #007bff; } .modal-header form input[type="text"]:focus, .modal-header form input[type="number"]:focus, .modal-header form textarea:focus { outline: none; border: 1px solid #007bff; } Лістинг 20 edit.css body{ background-color: #e2f1ff; } form { margin: 20px auto; padding: 20px; background-color: #ffffff; border: 1px solid #dee2e6; border-radius: 5px; width: 40%; } input, textarea { margin: 8px 0; padding: 10px; border: 1px solid #ced4da; border-radius: 5px; width: 100%; } button { background-color: #0078fa; color: #ffffff; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; display: block; 43
  • 45. margin: 0 auto; } button:hover { background-color: #0154ad; } label { font-weight: bold; } textarea { height: 150px; } input[type="file"] { margin-top: 10px; } 44
  • 46. Висновки Виконуючи цей проєкт, був розроблений web-додаток для обслуговування клієнтів та зберігання інформації щодо наданих послуг онлайн бібліотеки. Додаток забезпечує автоматизовану реєстрацію користувачів для отримання доступу до бібліотечних ресурсів та зручний доступ для адміністратора для керування бібліотекою. Основні функціональні можливості додатку включають: 1. Реєстрація користувачів: Додаток дозволяє користувачам створювати облікові записи, заповнюючи необхідні дані. Пошук та перегляд книг: Користувачі можуть шукати книги за різними критеріями, такими як назва, жанр. Після знаходження книги, користувач може переглянути її опис, обкладинку та іншу відповідну інформацію. 2. Керування каталогом книг: Адміністратор бібліотеки має доступ до панелі адміністрування, де він може додавати нові книги, редагувати існуючі записи книг. Аутентифікація та авторизація: Додаток забезпечує механізми аутентифікації користувачів для забезпечення безпеки доступу до функціональності бібліотеки. 45
  • 47. СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ 1. Kathy Sierra, Bert Bates. Head first java, 2nd edition. O`REILLY 2012 2. Дінеж Раджпут. Spring. Всі паттерни програмування. Pakt 2019 3. Balaji Varanasi. Introducing Maven: A Build Tool for Today's Java Developers 2nd ed. Apress 2019 4. Використання бібліотеки Lombok: веб-сайт. URL: https://projectlombok.org/features/ 5. Приклади застосування spring: веб-сайт. URL: https://spring.io/guides 6. Застосування maven: веб-сайт. URL: https://maven.apache.org/ 7. Використання шаблонізатора thymeleaf: веб-сайт. URL: https://www.thymeleaf.org/documentation.html 46
  • 48. ДОДАТОК А Рисунок 1 — головна сторінка Рисунок 2 — сторінка авторизації 47
  • 49. Рисунок 3 — сторінка регістрації Рисунок 4 — сторінка тексту книги 48
  • 50. Рисунок 5 — сторінка аміністратора Рисунок 6 — вікно додавання книги 49
  • 51. Рисунок 7 — сторінка редагування книги Рисунок 8 — вікно видалення книги 50