Contenu connexe Similaire à Курсовая (1).pdf (20) Plus de ssuser0562f1 (14) Курсовая (1).pdf1. Національний університет кораблебудування імені адмірала Макарова
Навчально-науковий інститут комп'ютерних наук та управління проектами
КУРСОВИЙ ПРОЕКТ (РОБОТА)
з ” Технології розробки програмного забезпечення на
сучасних платформах”
(назва дисципліни)
на тему: Онлайн бібліотека
Студента 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. Читання книг: Користувачі мають можливість відкривати електронні книги
для читання в онлайн-режимі.
10. ДІАГРАМА CТАНІВ
Діаграма станів для класу UserService.java представлена на рисунку 5
9
ДІАГРАМА CТАНІВ
Діаграма станів для класу BookController.java представлена на рисунку 4.
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
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
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
49. Рисунок 3 — сторінка регістрації
Рисунок 4 — сторінка тексту книги
48
50. Рисунок 5 — сторінка аміністратора
Рисунок 6 — вікно додавання книги
49
51. Рисунок 7 — сторінка редагування книги
Рисунок 8 — вікно видалення книги
50