├── .circleci └── config.yml ├── .gitignore ├── Procfile ├── README.md ├── data.social.network ├── pom.xml └── src │ └── main │ └── java │ └── info │ └── cheremisin │ └── social │ └── network │ ├── constants │ ├── Constants.java │ └── Gender.java │ ├── converters │ ├── MessageDtoToMessageConverter.java │ ├── MessageToMessageDtoConverter.java │ ├── PageToPageDtoUserConverter.java │ ├── UserDtoToUserConverter.java │ └── UserToUserDtoConverter.java │ ├── dto │ ├── MessageDTO.java │ ├── PageDTO.java │ ├── PasswordChangeDTO.java │ └── UserDTO.java │ ├── entities │ ├── Friendship.java │ ├── Message.java │ ├── Role.java │ └── User.java │ ├── exceptions │ ├── SocialNetworkException.java │ └── UserNotFoundException.java │ ├── repositories │ ├── FriendshipRepository.java │ ├── MessageRepository.java │ ├── RoleRepository.java │ └── UserRepository.java │ ├── service │ ├── FriendsService.java │ ├── ImageService.java │ ├── MessagesService.java │ ├── UserService.java │ └── impl │ │ ├── FriendsServiceImpl.java │ │ ├── ImageServiceImpl.java │ │ ├── MessagesServiceImpl.java │ │ ├── UserDetailsServiceImpl.java │ │ └── UserServiceImpl.java │ └── validation │ ├── EmailValidator.java │ ├── FieldMatch.java │ ├── FieldMatchValidator.java │ ├── Password.java │ ├── PasswordValidator.java │ └── ValidEmail.java ├── docker ├── Dockerfile ├── mysql docker commands.md └── social network docker commands.md ├── pom.xml ├── screenshots ├── 1.png ├── 10.png ├── 11.png ├── 12.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png └── 9.png ├── system.properties └── web.social.network ├── pom.xml └── src ├── main ├── java │ └── info │ │ └── cheremisin │ │ └── social │ │ └── network │ │ ├── SocialNetworkApplication.java │ │ ├── config │ │ ├── ApplicationSecurityConfig.java │ │ ├── BcryptPasswordEncoderConfig.java │ │ ├── CustomAuthenticationSuccessHandler.java │ │ └── LocalizationConfiguration.java │ │ ├── controllers │ │ ├── AdminController.java │ │ ├── FriendsController.java │ │ ├── IndexController.java │ │ ├── MessagesController.java │ │ ├── ProfileController.java │ │ ├── SettingsController.java │ │ └── UsersController.java │ │ ├── exceptionHandling │ │ └── ResponseExceptionHandler.java │ │ ├── initializer │ │ └── ImageInitializer.java │ │ └── utils │ │ └── ServerUtils.java └── resources │ ├── application-dev.properties │ ├── application-docker.properties │ ├── application-prod.properties │ ├── application-qa.properties │ ├── application.properties │ ├── data.sql │ ├── logback-spring.xml │ ├── messages.properties │ ├── messages_ru.properties │ ├── profileImages │ ├── 1.jpg │ ├── 10.jpg │ ├── 11.jpg │ ├── 12.jpeg │ ├── 2.png │ ├── 3.jpeg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── 7.jpg │ ├── 8.jpg │ └── 9.jpg │ ├── static │ ├── css │ │ ├── bootstrap.min.css │ │ ├── custom.css │ │ └── jquery-ui.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── images │ │ └── noname.svg │ └── js │ │ ├── bootstrap-notify.min.js │ │ ├── bootstrap.min.js │ │ ├── jquery-ui.min.js │ │ ├── jquery.min.js │ │ ├── notification.js │ │ ├── profile.js │ │ └── validation.js │ └── templates │ ├── conversation.html │ ├── friends.html │ ├── index.html │ ├── messages.html │ ├── profile.html │ ├── service │ ├── access-denied.html │ ├── registration-confirmation.html │ ├── something-went-wrong.html │ └── user-not-found.html │ ├── settings.html │ ├── template.html │ └── users.html └── test └── java └── info └── cheremisin └── social └── network └── SocialNetworkApplicationTests.java /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Java Maven CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-java/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/openjdk:11-jdk 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/postgres:9.4 16 | 17 | working_directory: ~/repo 18 | 19 | environment: 20 | # Customize the JVM maximum heap limit 21 | MAVEN_OPTS: -Xmx3200m 22 | 23 | steps: 24 | - checkout 25 | 26 | # Download and cache dependencies 27 | - restore_cache: 28 | keys: 29 | - v1-dependencies-{{ checksum "pom.xml" }} 30 | # fallback to using the latest cache if no exact match is found 31 | - v1-dependencies- 32 | 33 | - run: mvn install -DskipTests 34 | 35 | - run: mvn dependency:go-offline 36 | 37 | - save_cache: 38 | paths: 39 | - ~/.m2 40 | key: v1-dependencies-{{ checksum "pom.xml" }} 41 | 42 | # run tests! 43 | - run: mvn integration-test -Dspring.profiles.active=dev -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | profileImages/ 4 | !.mvn/wrapper/maven-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -Dserver.port=$PORT $JAVA_OPTS -jar web.social.network/target/social-network.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Social Network Project 2 | 3 | - Circle CI build status: 4 | [![CircleCI](https://circleci.com/gh/dmcheremisin/SpringBootSocialNetwork.svg?style=svg&circle-token=54d82edd98892db8d4e69740d9bee65e48242495)](https://circleci.com/gh/dmcheremisin/SpringBootSocialNetwork) 5 | 6 | ## Introduction 7 | This project is a continuation of the previous one. The previous project was made with pure Servlets and JSP 8 | technologies [details here](https://github.com/dmcheremisin/SocialNetwork). I used the same 9 | template layout and created a new project using SpringBoot. 10 | 11 | ## Functionality 12 | ### Base 13 | - Login / Logout 14 | - Register 15 | - View own profile 16 | - View other users profiles 17 | - Send messages 18 | - Add and remove friends 19 | - Accept and decline friends requests 20 | - Update profile information 21 | - Change password 22 | - Search for other users 23 | - View other users friends lists 24 | - View last messages 25 | - Drag and drop profile image upload 26 | - Internationalization: English and Russian languages 27 | - Localization 28 | 29 | ### Admin 30 | - Make other user admin 31 | - Block user 32 | 33 | ## Technologies used 34 | ### Stack: 35 | - Java 11 36 | - Spring: SpringBoot, MVC, Data JPA, Security, DevTools, Actuator 37 | - Maven 38 | - H2, Mysql, ClearDb(Heroku) 39 | - Thymeleaf 40 | - Javascript, jQuery 41 | - Html, CSS, Bootstrap 42 | - Test: JUnit, Mockito 43 | 44 | ### Environment 45 | - Heroku as CD tool 46 | - Circle CI as CI tool 47 | - Docker for containerization 48 | 49 | ## Application demo is available on Heroku 50 | Link: [https://springboot-social-network.herokuapp.com/](https://springboot-social-network.herokuapp.com/) 51 | Cersei is average user and Tyrion Lannister is super admin. Tyrion can't be blocked or made average admin user. 52 | 53 | Credentials for Cersei Lannister: 54 | - login cersei@lannister.ru 55 | - password fun123 56 | 57 | Tyrion Lannister credentials: 58 | - login tyrion@lannister.ru 59 | - password fun123 60 | 61 | ## Quick start 62 | 63 | ### Required: 64 | - Java 11 65 | - Maven 66 | 67 | ### Steps: 68 | 1. git clone https://github.com/dmcheremisin/SpringBootSocialNetwork.git 69 | 2. mvn clean package -DskipTests=true 70 | 3. cd web.social.network/target 71 | 4. java -jar -Dspring.profiles.active=dev social-network.jar 72 | 5. Go to http://localhost:8080 73 | 74 | ## Application profiles 75 | - dev - profile for development, uses embedded H2 database 76 | - qa - profile for qa testing, uses Mysql either local, or from docker container: [mysql docker container](https://github.com/dmcheremisin/SpringBootSocialNetwork/blob/master/docker/mysql%20docker%20commands.md) 77 | - prod - profile for Heroku, uses ClearDb. ClearDb is Heroku analog of Mysql: [details](https://devcenter.heroku.com/articles/cleardb) 78 | - docker - profile for Docker. It is created to test connection between 2 containers: mysql and social-network(spring 79 | boot app) 80 | 81 | ## Docker 82 | This application is tested in Docker. 83 | I made 2 containers: mysql and social-network, and then linked them. 84 | Commands for images creation and containers startup may be found in the directory: [docker](https://github.com/dmcheremisin/SpringBootSocialNetwork/tree/master/docker) 85 | 86 | ## Screenshots 87 | ![1](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/1.png) 88 | ![2](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/2.png) 89 | ![3](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/3.png) 90 | ![4](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/4.png) 91 | ![5](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/5.png) 92 | ![6](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/6.png) 93 | ![7](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/7.png) 94 | ![8](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/8.png) 95 | ![9](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/9.png) 96 | ![10](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/10.png) 97 | ![11](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/11.png) 98 | ![12](https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/master/screenshots/12.png) 99 | 100 | ### Todo: 101 | - Improve test coverage. Yes, I know it is very important, but I don't have enough time. -------------------------------------------------------------------------------- /data.social.network/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | social-network 7 | info.cheremisin 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | data.social.network 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-web 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-data-jpa 22 | 23 | 24 | com.h2database 25 | h2 26 | runtime 27 | 28 | 29 | mysql 30 | mysql-connector-java 31 | runtime 32 | 33 | 34 | org.springframework.security 35 | spring-security-core 36 | 37 | 38 | org.projectlombok 39 | lombok 40 | true 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/constants/Constants.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.constants; 2 | 3 | public class Constants { 4 | 5 | public static final String ROLE_USER = "ROLE_USER"; 6 | public static final String ROLE_ADMIN= "ROLE_ADMIN"; 7 | 8 | public static final String PROFILE_IMAGES = "profileImages"; 9 | } 10 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/constants/Gender.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.constants; 2 | 3 | public enum Gender { 4 | MALE(1), 5 | FEMALE(2), 6 | UNKNOWN(3); 7 | 8 | private final int gender; 9 | 10 | Gender(int sex) { 11 | gender = sex; 12 | } 13 | 14 | public static Gender getGenderById(Integer id) { 15 | if (id == null) 16 | return UNKNOWN; 17 | 18 | switch (id) { 19 | case 1: 20 | return MALE; 21 | case 2: 22 | return FEMALE; 23 | default: 24 | return UNKNOWN; 25 | } 26 | } 27 | 28 | public static int getGenderByName(String name) { 29 | if (name == null) 30 | return UNKNOWN.gender; 31 | 32 | switch (name) { 33 | case "Male": 34 | return MALE.gender; 35 | case "Female": 36 | return FEMALE.gender; 37 | default: 38 | return UNKNOWN.gender; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/converters/MessageDtoToMessageConverter.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.converters; 2 | 3 | import info.cheremisin.social.network.dto.MessageDTO; 4 | import info.cheremisin.social.network.entities.Message; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.core.convert.converter.Converter; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @RequiredArgsConstructor 11 | public class MessageDtoToMessageConverter implements Converter { 12 | 13 | private final UserDtoToUserConverter userDtoToUserConverter; 14 | 15 | @Override 16 | public Message convert(MessageDTO messageDTO) { 17 | if(messageDTO == null) { 18 | return null; 19 | } 20 | 21 | return Message.builder() 22 | .time(messageDTO.getTime()) 23 | .message(messageDTO.getMessage()) 24 | .sender(userDtoToUserConverter.convert(messageDTO.getSender())) 25 | .receiver(userDtoToUserConverter.convert(messageDTO.getReceiver())) 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/converters/MessageToMessageDtoConverter.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.converters; 2 | 3 | import info.cheremisin.social.network.dto.MessageDTO; 4 | import info.cheremisin.social.network.entities.Message; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @RequiredArgsConstructor 10 | public class MessageToMessageDtoConverter { 11 | 12 | private final UserToUserDtoConverter userToUserDtoConverter; 13 | 14 | public MessageDTO convert(Message message, Long id) { 15 | if(message == null) { 16 | return null; 17 | } 18 | return MessageDTO.builder() 19 | .time(message.getTime()) 20 | .message(message.getMessage()) 21 | .receiver(userToUserDtoConverter.convert(message.getReceiver())) 22 | .sender(userToUserDtoConverter.convert(message.getSender())) 23 | .companionId(id.equals(message.getSender().getId()) ? 24 | message.getReceiver().getId() : message.getSender().getId()) 25 | .build(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/converters/PageToPageDtoUserConverter.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.converters; 2 | 3 | import info.cheremisin.social.network.dto.PageDTO; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.entities.User; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.core.convert.converter.Converter; 9 | import org.springframework.data.domain.Page; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * Created by Dmitrii on 03.10.2019. 17 | */ 18 | 19 | @Component 20 | @RequiredArgsConstructor 21 | public class PageToPageDtoUserConverter implements Converter, PageDTO> { 22 | 23 | private final UserToUserDtoConverter userToUserDtoConverter; 24 | 25 | @Override 26 | public PageDTO convert(Page page) { 27 | List list = page.getContent().stream() 28 | .map(e -> userToUserDtoConverter.convert(e)) 29 | .collect(Collectors.toList()); 30 | 31 | return PageDTO.builder() 32 | .content(list) 33 | .totalPages(page.getTotalPages()) 34 | .currentPage(page.getNumber()) 35 | .hasNext(page.hasNext()) 36 | .hasPrevious(page.hasPrevious()) 37 | .build(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/converters/UserDtoToUserConverter.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.converters; 2 | 3 | import info.cheremisin.social.network.constants.Gender; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.entities.User; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.core.convert.converter.Converter; 9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | @RequiredArgsConstructor 14 | public class UserDtoToUserConverter implements Converter { 15 | 16 | private final BCryptPasswordEncoder passwordEncoder; 17 | 18 | @Override 19 | public User convert(UserDTO userDTO) { 20 | User.UserBuilder builder = User.builder() 21 | .id(userDTO.getId()) 22 | .email(userDTO.getEmail()) 23 | .firstName(userDTO.getFirstName()) 24 | .lastName(userDTO.getLastName()) 25 | .phone(userDTO.getPhone()) 26 | .sex(Gender.getGenderByName(userDTO.getSex())) 27 | .image(userDTO.getImage()) 28 | .dob(userDTO.getDob()); 29 | 30 | if (userDTO.getPassword() != null) 31 | builder.password(passwordEncoder.encode(userDTO.getPassword())); 32 | 33 | return builder.build(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/converters/UserToUserDtoConverter.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.converters; 2 | 3 | import info.cheremisin.social.network.constants.Gender; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.entities.User; 6 | import org.springframework.core.convert.converter.Converter; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.util.CollectionUtils; 9 | 10 | import static info.cheremisin.social.network.constants.Constants.ROLE_ADMIN; 11 | 12 | @Component 13 | public class UserToUserDtoConverter implements Converter { 14 | 15 | @Override 16 | public UserDTO convert(User user) { 17 | if (user == null) 18 | return null; 19 | 20 | return UserDTO.builder() 21 | .id(user.getId()) 22 | .email(user.getEmail()) 23 | .password(user.getPassword()) 24 | .firstName(user.getFirstName()) 25 | .lastName(user.getLastName()) 26 | .phone(user.getPhone()) 27 | .sex(Gender.getGenderById(user.getSex()).name()) 28 | .isAdmin(user.getRoles().stream().anyMatch(r -> r.getName().equals(ROLE_ADMIN))) 29 | .image(user.getImage()) 30 | .dob(user.getDob()) 31 | .build(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/dto/MessageDTO.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.dto; 2 | 3 | import lombok.*; 4 | 5 | import javax.validation.constraints.NotNull; 6 | import javax.validation.constraints.Size; 7 | import java.time.LocalDateTime; 8 | 9 | @Getter 10 | @Setter 11 | @Builder 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public class MessageDTO { 15 | 16 | private LocalDateTime time; 17 | 18 | @NotNull 19 | @Size(min=3, max = 3000) 20 | private String message; 21 | private UserDTO sender; 22 | private UserDTO receiver; 23 | private Long companionId; 24 | } 25 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/dto/PageDTO.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Created by Dmitrii on 03.10.2019. 14 | */ 15 | @Getter 16 | @Setter 17 | @Builder 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class PageDTO { 21 | public List content; 22 | public Boolean hasNext; 23 | public Boolean hasPrevious; 24 | public Integer totalPages; 25 | public Integer currentPage; 26 | } 27 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/dto/PasswordChangeDTO.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.dto; 2 | 3 | import info.cheremisin.social.network.validation.FieldMatch; 4 | import info.cheremisin.social.network.validation.Password; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Getter; 8 | import lombok.NoArgsConstructor; 9 | import lombok.Setter; 10 | import lombok.ToString; 11 | 12 | import javax.validation.constraints.NotNull; 13 | import javax.validation.constraints.Size; 14 | 15 | @Getter 16 | @Setter 17 | @ToString 18 | @Builder 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @FieldMatch.List({ 22 | @FieldMatch(first = "password", second = "passwordConfirmation", message = "The password fields must match") 23 | }) 24 | public class PasswordChangeDTO { 25 | 26 | @NotNull 27 | @Size(min=6, max=100, message = "Min size is 6 and max size is 100") 28 | @Password 29 | private String oldPassword; 30 | 31 | @NotNull 32 | @Size(min=6, max=100, message = "Min size is 6 and max size is 100") 33 | @Password 34 | private String password; 35 | 36 | @NotNull 37 | @Size(min=6, max=100, message = "Min size is 6 and max size is 100") 38 | private String passwordConfirmation; 39 | } 40 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.dto; 2 | 3 | import info.cheremisin.social.network.validation.FieldMatch; 4 | import info.cheremisin.social.network.validation.Password; 5 | import info.cheremisin.social.network.validation.ValidEmail; 6 | import lombok.*; 7 | import org.springframework.format.annotation.DateTimeFormat; 8 | 9 | import javax.validation.constraints.NotNull; 10 | import javax.validation.constraints.PastOrPresent; 11 | import javax.validation.constraints.Size; 12 | import java.time.LocalDate; 13 | import java.util.Objects; 14 | 15 | @Getter 16 | @Setter 17 | @ToString 18 | @Builder 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @FieldMatch.List({ 22 | @FieldMatch(first = "password", second = "passwordConfirmation", message = "Password fields must match") 23 | }) 24 | public class UserDTO { 25 | 26 | private Long id; 27 | 28 | @NotNull 29 | @ValidEmail 30 | private String email; 31 | 32 | @NotNull 33 | @Size(min=6, max=100, message = "Min size is 6 and max size is 100") 34 | @Password 35 | private String password; 36 | 37 | @NotNull 38 | @Size(min=6, max=100, message = "Min size is 6 and max size is 100") 39 | private String passwordConfirmation; 40 | 41 | @NotNull 42 | @Size(min=2, max=100, message = "Min size is 2 and max size is 100") 43 | private String firstName; 44 | 45 | @NotNull 46 | @Size(min=2, max=100, message = "Min size is 2 and max size is 100") 47 | private String lastName; 48 | 49 | @PastOrPresent 50 | @DateTimeFormat(pattern = "dd.MM.yyyy") 51 | private LocalDate dob; 52 | 53 | private String sex; 54 | private String phone; 55 | private Boolean isAdmin; 56 | private String image; 57 | 58 | @Override 59 | public boolean equals(Object o) { 60 | if (this == o) return true; 61 | if (o == null || getClass() != o.getClass()) return false; 62 | UserDTO user = (UserDTO) o; 63 | return id.equals(user.id); 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | return Objects.hash(id); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/entities/Friendship.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.entities; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import javax.persistence.Column; 10 | import javax.persistence.Entity; 11 | import javax.persistence.GeneratedValue; 12 | import javax.persistence.GenerationType; 13 | import javax.persistence.Id; 14 | import javax.persistence.JoinColumn; 15 | import javax.persistence.ManyToOne; 16 | import javax.persistence.Table; 17 | 18 | @Getter 19 | @Setter 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @Entity 24 | @Table(name = "friendship") 25 | public class Friendship { 26 | 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | private Long id; 30 | 31 | @ManyToOne 32 | @JoinColumn(name = "user_sender") 33 | private User userSender; 34 | 35 | @ManyToOne 36 | @JoinColumn(name = "user_receiver") 37 | private User userReceiver; 38 | 39 | @Column(name = "accepted") 40 | private Boolean accepted; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/entities/Message.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.entities; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | import java.time.LocalDateTime; 7 | 8 | @Getter 9 | @Setter 10 | @Builder 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @Entity 14 | @Table(name = "messages") 15 | public class Message { 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private int id; 20 | 21 | @Column(name = "time") 22 | private LocalDateTime time; 23 | 24 | @Column(name = "message") 25 | private String message; 26 | 27 | @ManyToOne 28 | @JoinColumn(name = "sender") 29 | private User sender; 30 | 31 | @ManyToOne 32 | @JoinColumn(name = "receiver") 33 | private User receiver; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/entities/Role.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.entities; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | import javax.persistence.Column; 10 | import javax.persistence.Entity; 11 | import javax.persistence.GeneratedValue; 12 | import javax.persistence.GenerationType; 13 | import javax.persistence.Id; 14 | import javax.persistence.Table; 15 | 16 | @Getter 17 | @Setter 18 | @Builder 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @Entity 22 | @Table(name = "role") 23 | public class Role { 24 | 25 | @Id 26 | @GeneratedValue(strategy = GenerationType.IDENTITY) 27 | private Long id; 28 | 29 | @Column(name = "name") 30 | private String name; 31 | } 32 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/entities/User.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.entities; 2 | 3 | import lombok.*; 4 | 5 | import javax.persistence.*; 6 | import java.time.LocalDate; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Objects; 10 | 11 | @Getter 12 | @Setter 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | @Entity 17 | @Table(name = "user") 18 | public class User { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) 22 | private Long id; 23 | 24 | @Column(name = "username", unique = true) 25 | private String email; 26 | 27 | @Column(name = "password") 28 | private String password; 29 | 30 | @Column(name = "first_name") 31 | private String firstName; 32 | 33 | @Column(name = "last_name") 34 | private String lastName; 35 | 36 | @Column(name = "date_of_birth") 37 | private LocalDate dob; 38 | 39 | @Column(name = "sex") 40 | private Integer sex; 41 | 42 | @Column(name = "phone") 43 | private String phone; 44 | 45 | @Column(name = "image") 46 | private String image; 47 | 48 | @ManyToMany(fetch = FetchType.EAGER) 49 | @JoinTable(name = "user_role", 50 | joinColumns = @JoinColumn(name = "user_id"), 51 | inverseJoinColumns = @JoinColumn(name = "role_id") 52 | ) 53 | private List roles = new ArrayList<>(); 54 | 55 | @Override 56 | public boolean equals(Object o) { 57 | if (this == o) return true; 58 | if (o == null || getClass() != o.getClass()) return false; 59 | User user = (User) o; 60 | return id.equals(user.id); 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return Objects.hash(id); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/exceptions/SocialNetworkException.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.exceptions; 2 | 3 | public class SocialNetworkException extends RuntimeException { 4 | 5 | public SocialNetworkException() { 6 | } 7 | 8 | public SocialNetworkException(String message) { 9 | super(message); 10 | } 11 | 12 | public SocialNetworkException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public SocialNetworkException(Throwable cause) { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/exceptions/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.exceptions; 2 | 3 | public class UserNotFoundException extends SocialNetworkException { 4 | 5 | public UserNotFoundException() { 6 | } 7 | 8 | public UserNotFoundException(String message) { 9 | super(message); 10 | } 11 | 12 | public UserNotFoundException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public UserNotFoundException(Throwable cause) { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/repositories/FriendshipRepository.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.repositories; 2 | 3 | import info.cheremisin.social.network.entities.Friendship; 4 | import info.cheremisin.social.network.entities.User; 5 | import org.springframework.data.jpa.repository.Modifying; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.CrudRepository; 8 | import org.springframework.data.repository.query.Param; 9 | 10 | import java.util.List; 11 | 12 | public interface FriendshipRepository extends CrudRepository { 13 | 14 | List findAllByUserSenderIdOrUserReceiverId(Long userSenderId, Long userReceiverId); 15 | 16 | @Query("SELECT f from Friendship f WHERE (f.userSender.id = :userId OR f.userReceiver.id = :userId) " + 17 | "AND f.accepted = true") 18 | List findAcceptedFriendshipUsers(@Param("userId") Long userId); 19 | 20 | @Modifying(clearAutomatically = true) 21 | @Query("DELETE FROM Friendship f WHERE (f.userSender = :user AND f.userReceiver = :friend) " + 22 | "OR (f.userSender = :friend AND f.userReceiver = :user)") 23 | void deleteFriendRequests(@Param("user") User user, @Param("friend") User friend); 24 | 25 | @Modifying(clearAutomatically = true) 26 | @Query(value = "insert into friendship (user_sender, user_receiver, accepted) VALUES (:user, :friend, true)", 27 | nativeQuery = true) 28 | void addFriendship(@Param("user") User user, @Param("friend") User friend); 29 | 30 | @Modifying(clearAutomatically = true) 31 | @Query(value = "insert into friendship (user_sender, user_receiver, accepted) VALUES (:user, :friend, false)", 32 | nativeQuery = true) 33 | void addToFriends(@Param("user") User user, @Param("friend") User friend); 34 | 35 | @Query("SELECT case when count(f)> 0 then true else false end FROM Friendship f WHERE (f.userSender = :user AND f.userReceiver = :friend) " + 36 | "OR (f.userSender = :friend AND f.userReceiver = :user)") 37 | boolean checkFriendshipExists(@Param("user") User user, @Param("friend") User friend); 38 | } 39 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/repositories/MessageRepository.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.repositories; 2 | 3 | import info.cheremisin.social.network.entities.Message; 4 | import org.springframework.data.jpa.repository.Query; 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.data.repository.query.Param; 7 | 8 | import java.util.List; 9 | 10 | public interface MessageRepository extends CrudRepository { 11 | 12 | @Query(value="SELECT m FROM Message m WHERE m.id IN (SELECT MAX(id) FROM Message GROUP BY sender, receiver) " + 13 | " AND (m.sender.id = :id OR m.receiver.id = :id)") 14 | List findAllRecentMessages(@Param("id") Long id); 15 | 16 | @Query(value="SELECT m FROM Message m WHERE (m.sender.id = :userId AND m.receiver.id = :companionId) OR " + 17 | "(m.sender.id = :companionId AND m.receiver.id = :userId) ORDER BY m.time") 18 | List findConversation(@Param("userId") Long userId, @Param("companionId") Long companionId); 19 | 20 | Message findFirstBySenderIdOrReceiverIdOrderByIdDesc(Long userId, Long theSameUserId); 21 | } 22 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/repositories/RoleRepository.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.repositories; 2 | 3 | import info.cheremisin.social.network.entities.Role; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | public interface RoleRepository extends CrudRepository { 7 | 8 | Role getRoleByName(String name); 9 | } 10 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.repositories; 2 | 3 | import info.cheremisin.social.network.entities.User; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.Modifying; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.CrudRepository; 9 | import org.springframework.data.repository.PagingAndSortingRepository; 10 | import org.springframework.data.repository.query.Param; 11 | 12 | import java.time.LocalDate; 13 | 14 | public interface UserRepository extends PagingAndSortingRepository { 15 | 16 | User findUserByEmail(String email); 17 | 18 | Page findAllByIdNot(Long Id, Pageable pageable); 19 | 20 | @Query(value="SELECT u FROM User u WHERE u.id <> :id AND ( LOWER(u.firstName) LIKE :search OR LOWER(u.lastName) LIKE :search )") 21 | Page findAllWithSearch(@Param("id") Long id, @Param("search") String search, Pageable pageable); 22 | 23 | @Modifying(clearAutomatically = true) 24 | @Query("UPDATE User u SET u.firstName = :firstName, u.lastName = :lastName," + 25 | "u.dob = :dob, u.sex = :sex, u.phone = :phone WHERE u.id = :id") 26 | int updateUserSettings(@Param("firstName") String firstName, @Param("lastName") String lastName, 27 | @Param("dob") LocalDate dob, @Param("sex") Integer sex, @Param("phone") String phone, 28 | @Param("id") Long id); 29 | 30 | @Modifying(clearAutomatically = true) 31 | @Query("UPDATE User u SET u.password = :password WHERE u.id = :id") 32 | int updatePassword(@Param("password") String password, @Param("id") Long id); 33 | } 34 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/FriendsService.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service; 2 | 3 | import info.cheremisin.social.network.dto.UserDTO; 4 | 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | public interface FriendsService { 9 | 10 | Map> getFriends(Long userId, String search); 11 | 12 | Set getAcceptedFriendshipUsers(Long id); 13 | 14 | void deleteFriendship(UserDTO userDTO, Long friendId); 15 | 16 | void acceptFriendship(UserDTO userDTO, Long friendId); 17 | 18 | void addToFriends(UserDTO user, Long friendId); 19 | 20 | Boolean checkFriendship(UserDTO user, UserDTO friend); 21 | } 22 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/ImageService.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service; 2 | 3 | import info.cheremisin.social.network.dto.UserDTO; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Path; 8 | 9 | public interface ImageService { 10 | 11 | String updateProfileImage(UserDTO user, MultipartFile multipartFile) throws IOException; 12 | 13 | Path getProfileImagesPath(); 14 | } 15 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/MessagesService.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service; 2 | 3 | import info.cheremisin.social.network.dto.MessageDTO; 4 | 5 | import java.util.Collection; 6 | import java.util.List; 7 | 8 | public interface MessagesService { 9 | Collection findAllRecentMessages(Long id); 10 | 11 | List findConversation(Long userId, Long companionId); 12 | 13 | MessageDTO getRecentMessage(Long id); 14 | 15 | void postMessage(MessageDTO messageDTO); 16 | } 17 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/UserService.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service; 2 | 3 | import info.cheremisin.social.network.dto.PageDTO; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.entities.User; 6 | import org.springframework.data.domain.Pageable; 7 | 8 | public interface UserService { 9 | 10 | User getUser(Long id); 11 | 12 | UserDTO getUserById(Long id); 13 | 14 | UserDTO getUserByEmail(String email); 15 | 16 | PageDTO findAllPageable(Long id, Pageable pageable); 17 | 18 | PageDTO findAllWithSearch(Long id, String search, Pageable pageable); 19 | 20 | void createUser(UserDTO userDTO); 21 | 22 | void updatePassword(String password, Long id); 23 | 24 | void updateUser(UserDTO userDTO); 25 | 26 | void updateUserImage(UserDTO userDTO, String fileName); 27 | 28 | void makeUserAdmin(Long userId); 29 | 30 | void blockUser(Long userId); 31 | } 32 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/impl/FriendsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service.impl; 2 | 3 | import info.cheremisin.social.network.converters.UserDtoToUserConverter; 4 | import info.cheremisin.social.network.converters.UserToUserDtoConverter; 5 | import info.cheremisin.social.network.dto.UserDTO; 6 | import info.cheremisin.social.network.entities.Friendship; 7 | import info.cheremisin.social.network.entities.User; 8 | import info.cheremisin.social.network.repositories.FriendshipRepository; 9 | import info.cheremisin.social.network.service.FriendsService; 10 | import info.cheremisin.social.network.service.UserService; 11 | import lombok.RequiredArgsConstructor; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.transaction.annotation.Transactional; 14 | import org.springframework.util.StringUtils; 15 | 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Set; 20 | import java.util.function.Predicate; 21 | import java.util.stream.Collectors; 22 | 23 | @Service 24 | @RequiredArgsConstructor 25 | public class FriendsServiceImpl implements FriendsService { 26 | 27 | private final FriendshipRepository friendshipRepository; 28 | private final UserToUserDtoConverter userToUserDtoConverter; 29 | private final UserDtoToUserConverter userDtoToUserConverter; 30 | private final UserService userService; 31 | 32 | @Override 33 | @Transactional(readOnly = true) 34 | public Map> getFriends(Long userId, String search) { 35 | List requests = friendshipRepository.findAllByUserSenderIdOrUserReceiverId(userId, userId); 36 | Map> requestMap = requests.stream() 37 | .collect(Collectors.partitioningBy(Friendship::getAccepted)); 38 | 39 | Predicate userPredicate = u -> StringUtils.isEmpty(search) || 40 | (u.getFirstName() + ' ' + u.getLastName()).toLowerCase().contains(search.toLowerCase()); 41 | Set usersNotAcceptedRequests = requestMap.get(false).stream() 42 | .filter(r -> r.getUserSender().getId().equals(userId)) 43 | .map(Friendship::getUserReceiver) 44 | .filter(userPredicate) 45 | .map(userToUserDtoConverter::convert) 46 | .collect(Collectors.toSet()); 47 | 48 | Set notAcceptedRequestsToUser = requestMap.get(false).stream() 49 | .filter(r -> r.getUserReceiver().getId().equals(userId)) 50 | .map(Friendship::getUserSender) 51 | .filter(userPredicate) 52 | .map(userToUserDtoConverter::convert) 53 | .collect(Collectors.toSet()); 54 | 55 | Set friendsOfUser = requestMap.get(true).stream() 56 | .map(r -> r.getUserSender().getId().equals(userId) ? r.getUserReceiver() : r.getUserSender()) 57 | .filter(userPredicate) 58 | .map(userToUserDtoConverter::convert) 59 | .collect(Collectors.toSet()); 60 | 61 | Map> map = new HashMap<>(); 62 | map.put("usersNotAcceptedRequests", usersNotAcceptedRequests); 63 | map.put("notAcceptedRequestsToUser", notAcceptedRequestsToUser); 64 | map.put("friendsOfUser", friendsOfUser); 65 | 66 | return map; 67 | } 68 | 69 | @Override 70 | @Transactional(readOnly = true) 71 | public Set getAcceptedFriendshipUsers(Long id) { 72 | List friends = friendshipRepository.findAcceptedFriendshipUsers(id); 73 | return friends.stream() 74 | .map(r -> r.getUserSender().getId().equals(id) ? r.getUserReceiver() : r.getUserSender()) 75 | .map(userToUserDtoConverter::convert) 76 | .collect(Collectors.toSet()); 77 | } 78 | 79 | @Override 80 | @Transactional 81 | public void deleteFriendship(UserDTO userDTO, Long friendId) { 82 | User user = userDtoToUserConverter.convert(userDTO); 83 | User friend = userService.getUser(friendId); 84 | friendshipRepository.deleteFriendRequests(user, friend); 85 | } 86 | 87 | @Override 88 | @Transactional 89 | public void acceptFriendship(UserDTO userDTO, Long friendId) { 90 | User user = userDtoToUserConverter.convert(userDTO); 91 | User friend = userService.getUser(friendId); 92 | friendshipRepository.deleteFriendRequests(user, friend); 93 | friendshipRepository.addFriendship(user, friend); 94 | } 95 | 96 | @Override 97 | @Transactional 98 | public void addToFriends(UserDTO userDTO, Long friendId) { 99 | User user = userDtoToUserConverter.convert(userDTO); 100 | User friend = userService.getUser(friendId); 101 | friendshipRepository.addToFriends(user, friend); 102 | } 103 | 104 | @Override 105 | @Transactional(readOnly = true) 106 | public Boolean checkFriendship(UserDTO userDTO, UserDTO friendDTO) { 107 | User user = userDtoToUserConverter.convert(userDTO); 108 | User friend = userDtoToUserConverter.convert(friendDTO); 109 | return friendshipRepository.checkFriendshipExists(user, friend); 110 | } 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/impl/ImageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service.impl; 2 | 3 | import info.cheremisin.social.network.dto.UserDTO; 4 | import info.cheremisin.social.network.service.ImageService; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.web.multipart.MultipartFile; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | 13 | import static info.cheremisin.social.network.constants.Constants.PROFILE_IMAGES; 14 | 15 | @Service 16 | public class ImageServiceImpl implements ImageService { 17 | 18 | private static String getNewFileName(UserDTO user, MultipartFile multipartFile) { 19 | String fileName = multipartFile.getOriginalFilename(); 20 | String extension = getFileExtension(fileName); 21 | return user.getId() + "." + extension; 22 | } 23 | 24 | private static String getFileExtension(String fileName) { 25 | int index = fileName.lastIndexOf('.'); 26 | return index == -1 ? fileName : fileName.substring(index + 1); 27 | } 28 | 29 | @Override 30 | public Path getProfileImagesPath() { 31 | return Paths.get(".").resolve(PROFILE_IMAGES); 32 | } 33 | 34 | @Override 35 | public String updateProfileImage(UserDTO user, MultipartFile multipartFile) throws IOException { 36 | byte[] bytes = multipartFile.getBytes(); 37 | Path pathImages = getProfileImagesPath(); 38 | Files.delete(pathImages.resolve(user.getImage())); 39 | String newFileName = getNewFileName(user, multipartFile); 40 | Files.write(pathImages.resolve(newFileName), bytes); 41 | return newFileName; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/impl/MessagesServiceImpl.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service.impl; 2 | 3 | import info.cheremisin.social.network.converters.MessageDtoToMessageConverter; 4 | import info.cheremisin.social.network.converters.MessageToMessageDtoConverter; 5 | import info.cheremisin.social.network.dto.MessageDTO; 6 | import info.cheremisin.social.network.entities.Message; 7 | import info.cheremisin.social.network.entities.User; 8 | import info.cheremisin.social.network.repositories.MessageRepository; 9 | import info.cheremisin.social.network.service.MessagesService; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.*; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class MessagesServiceImpl implements MessagesService { 19 | 20 | private final MessageRepository messageRepository; 21 | private final MessageToMessageDtoConverter messageToMessageDtoConverter; 22 | private final MessageDtoToMessageConverter messageDtoToMessageConverter; 23 | 24 | @Override 25 | @Transactional(readOnly = true) 26 | public Collection findAllRecentMessages(Long id) { 27 | Iterable all = messageRepository.findAllRecentMessages(id); 28 | Map map = new HashMap<>(); 29 | all.forEach(m -> { 30 | MessageDTO messageDTO = messageToMessageDtoConverter.convert(m, id); 31 | User user = m.getSender().getId().equals(id) ? m.getReceiver() : m.getSender(); 32 | map.put(user, messageDTO); 33 | }); 34 | return map.values(); 35 | } 36 | 37 | @Override 38 | @Transactional(readOnly = true) 39 | public List findConversation(Long userId, Long companionId) { 40 | List all = messageRepository.findConversation(userId, companionId); 41 | List messages = new LinkedList<>(); 42 | all.forEach(m -> messages.add(messageToMessageDtoConverter.convert(m, userId))); 43 | return messages; 44 | } 45 | 46 | @Override 47 | @Transactional(readOnly = true) 48 | public MessageDTO getRecentMessage(Long id) { 49 | Message message = messageRepository.findFirstBySenderIdOrReceiverIdOrderByIdDesc(id, id); 50 | MessageDTO messageDTO = messageToMessageDtoConverter.convert(message, id); 51 | return messageDTO; 52 | } 53 | 54 | @Override 55 | @Transactional 56 | public void postMessage(MessageDTO messageDTO) { 57 | Message message = messageDtoToMessageConverter.convert(messageDTO); 58 | messageRepository.save(message); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/impl/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service.impl; 2 | 3 | import info.cheremisin.social.network.entities.Role; 4 | import info.cheremisin.social.network.entities.User; 5 | import info.cheremisin.social.network.repositories.UserRepository; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 8 | import org.springframework.security.core.userdetails.UserDetails; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.Collection; 15 | import java.util.stream.Collectors; 16 | 17 | @Service 18 | public class UserDetailsServiceImpl implements UserDetailsService { 19 | 20 | private UserRepository userRepository; 21 | 22 | public UserDetailsServiceImpl(UserRepository userRepository) { 23 | this.userRepository = userRepository; 24 | } 25 | 26 | @Override 27 | @Transactional(readOnly = true) 28 | public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { 29 | User user = userRepository.findUserByEmail(email); 30 | if (user == null) 31 | throw new UsernameNotFoundException("Invalid username or password."); 32 | 33 | return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), 34 | mapRolesToAuthorities(user.getRoles())); 35 | } 36 | 37 | private static Collection mapRolesToAuthorities(Collection roles) { 38 | return roles.stream() 39 | .map(role -> new SimpleGrantedAuthority(role.getName())) 40 | .collect(Collectors.toList()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.service.impl; 2 | 3 | import info.cheremisin.social.network.constants.Gender; 4 | import info.cheremisin.social.network.converters.PageToPageDtoUserConverter; 5 | import info.cheremisin.social.network.converters.UserDtoToUserConverter; 6 | import info.cheremisin.social.network.converters.UserToUserDtoConverter; 7 | import info.cheremisin.social.network.dto.PageDTO; 8 | import info.cheremisin.social.network.dto.UserDTO; 9 | import info.cheremisin.social.network.entities.Role; 10 | import info.cheremisin.social.network.entities.User; 11 | import info.cheremisin.social.network.exceptions.SocialNetworkException; 12 | import info.cheremisin.social.network.exceptions.UserNotFoundException; 13 | import info.cheremisin.social.network.repositories.RoleRepository; 14 | import info.cheremisin.social.network.repositories.UserRepository; 15 | import info.cheremisin.social.network.service.UserService; 16 | import lombok.RequiredArgsConstructor; 17 | import org.springframework.data.domain.Page; 18 | import org.springframework.data.domain.Pageable; 19 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 20 | import org.springframework.stereotype.Service; 21 | import org.springframework.transaction.annotation.Transactional; 22 | 23 | import java.util.ArrayList; 24 | 25 | import static info.cheremisin.social.network.constants.Constants.ROLE_ADMIN; 26 | import static info.cheremisin.social.network.constants.Constants.ROLE_USER; 27 | 28 | @Service 29 | @RequiredArgsConstructor 30 | public class UserServiceImpl implements UserService { 31 | 32 | private final UserRepository userRepository; 33 | private final RoleRepository roleRepository; 34 | private final BCryptPasswordEncoder passwordEncoder; 35 | private final UserToUserDtoConverter userToUserDtoConverter; 36 | private final UserDtoToUserConverter userDtoToUserConverter; 37 | private final PageToPageDtoUserConverter pageToPageDtoUserConverter; 38 | 39 | private static void checkSuperAdmin(User user) { 40 | if (user.getId() == 1) 41 | throw new SocialNetworkException("It is not allowed to change roles for the user with id = 1"); 42 | } 43 | 44 | @Override 45 | @Transactional(readOnly = true) 46 | public User getUser(Long id) { 47 | return userRepository.findById(id) 48 | .orElseThrow(() -> 49 | new UserNotFoundException(String.format("User with id = %s is not found", id))); 50 | } 51 | 52 | @Override 53 | @Transactional(readOnly = true) 54 | public UserDTO getUserById(Long id) { 55 | User user = getUser(id); 56 | return userToUserDtoConverter.convert(user); 57 | } 58 | 59 | @Override 60 | @Transactional(readOnly = true) 61 | public UserDTO getUserByEmail(String email) { 62 | User user = userRepository.findUserByEmail(email); 63 | return userToUserDtoConverter.convert(user); 64 | } 65 | 66 | @Override 67 | @Transactional(readOnly = true) 68 | public PageDTO findAllPageable(Long id, Pageable pageable) { 69 | Page pagedUsers = userRepository.findAllByIdNot(id, pageable); 70 | return pageToPageDtoUserConverter.convert(pagedUsers); 71 | } 72 | 73 | @Override 74 | @Transactional(readOnly = true) 75 | public PageDTO findAllWithSearch(Long id, String search, Pageable pageable) { 76 | search = String.format("%%%s%%", search).toLowerCase(); 77 | Page pagedUsers = userRepository.findAllWithSearch(id, search, pageable); 78 | return pageToPageDtoUserConverter.convert(pagedUsers); 79 | } 80 | 81 | @Override 82 | @Transactional 83 | public void createUser(UserDTO userDTO) { 84 | User user = userDtoToUserConverter.convert(userDTO); 85 | user.setRoles(new ArrayList<>()); 86 | 87 | Role role = roleRepository.getRoleByName(ROLE_USER); 88 | user.getRoles().add(role); 89 | 90 | userRepository.save(user); 91 | } 92 | 93 | @Override 94 | @Transactional 95 | public void updatePassword(String password, Long id) { 96 | String encodedPassword = passwordEncoder.encode(password); 97 | userRepository.updatePassword(encodedPassword, id); 98 | } 99 | 100 | @Override 101 | @Transactional 102 | public void updateUser(UserDTO user) { 103 | int gender = Gender.getGenderByName(user.getSex()); 104 | userRepository.updateUserSettings(user.getFirstName(), user.getLastName(), user.getDob(), gender, 105 | user.getPhone(), user.getId()); 106 | } 107 | 108 | @Override 109 | @Transactional 110 | public void updateUserImage(UserDTO userDTO, String fileName) { 111 | User user = getUser(userDTO.getId()); 112 | user.setImage(fileName); 113 | userRepository.save(user); 114 | userDTO.setImage(fileName); 115 | } 116 | 117 | @Override 118 | @Transactional 119 | public void makeUserAdmin(Long userId) { 120 | User user = getUser(userId); 121 | checkSuperAdmin(user); 122 | Role role = roleRepository.getRoleByName(ROLE_ADMIN); 123 | user.getRoles().add(role); 124 | userRepository.save(user); 125 | } 126 | 127 | @Override 128 | @Transactional 129 | public void blockUser(Long userId) { 130 | User user = getUser(userId); 131 | checkSuperAdmin(user); 132 | user.setRoles(new ArrayList<>()); 133 | userRepository.save(user); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/validation/EmailValidator.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.validation; 2 | 3 | import javax.validation.ConstraintValidator; 4 | import javax.validation.ConstraintValidatorContext; 5 | 6 | public class EmailValidator implements ConstraintValidator { 7 | 8 | private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"; 9 | 10 | @Override 11 | public boolean isValid(final String email, final ConstraintValidatorContext context) { 12 | if (email == null) { 13 | return false; 14 | } 15 | return email.matches(EMAIL_PATTERN); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/validation/FieldMatch.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.validation; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Constraint(validatedBy = FieldMatchValidator.class) 12 | @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface FieldMatch { 16 | String message() default ""; 17 | Class[] groups() default {}; 18 | Class[] payload() default {}; 19 | 20 | String first(); 21 | String second(); 22 | 23 | @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Documented 26 | @interface List 27 | { 28 | FieldMatch[] value(); 29 | } 30 | } -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/validation/FieldMatchValidator.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.validation; 2 | 3 | import org.springframework.beans.BeanWrapperImpl; 4 | 5 | import javax.validation.ConstraintValidator; 6 | import javax.validation.ConstraintValidatorContext; 7 | 8 | public class FieldMatchValidator implements ConstraintValidator { 9 | 10 | private String firstFieldName; 11 | private String secondFieldName; 12 | private String message; 13 | 14 | @Override 15 | public void initialize(final FieldMatch constraintAnnotation) { 16 | firstFieldName = constraintAnnotation.first(); 17 | secondFieldName = constraintAnnotation.second(); 18 | message = constraintAnnotation.message(); 19 | } 20 | 21 | @Override 22 | public boolean isValid(final Object value, final ConstraintValidatorContext context) { 23 | boolean valid = true; 24 | try 25 | { 26 | final Object firstObj = new BeanWrapperImpl(value).getPropertyValue(firstFieldName); 27 | final Object secondObj = new BeanWrapperImpl(value).getPropertyValue(secondFieldName); 28 | 29 | valid = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj); 30 | } 31 | catch (final Exception ignore) 32 | { 33 | // we can ignore 34 | } 35 | 36 | if (!valid){ 37 | context.buildConstraintViolationWithTemplate(message) 38 | .addPropertyNode(firstFieldName) 39 | .addConstraintViolation() 40 | .disableDefaultConstraintViolation(); 41 | } 42 | 43 | return valid; 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/validation/Password.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.validation; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Constraint(validatedBy = PasswordValidator.class) 12 | @Target({ ElementType.TYPE, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface Password { 16 | String message() default "Invalid password"; 17 | 18 | Class[] groups() default {}; 19 | 20 | Class[] payload() default {}; 21 | } 22 | -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/validation/PasswordValidator.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.validation; 2 | 3 | import javax.validation.ConstraintValidator; 4 | import javax.validation.ConstraintValidatorContext; 5 | 6 | public class PasswordValidator implements ConstraintValidator { 7 | 8 | private static final String PASSWORD_PATTERN = "(?=.*[0-9].*)(?=.*[a-zA-Z].*)[0-9a-zA-Z]{6,}"; 9 | 10 | @Override 11 | public boolean isValid(final String password, final ConstraintValidatorContext context) { 12 | if (password == null) { 13 | return false; 14 | } 15 | return password.matches(PASSWORD_PATTERN); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /data.social.network/src/main/java/info/cheremisin/social/network/validation/ValidEmail.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.validation; 2 | 3 | import javax.validation.Constraint; 4 | import javax.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Constraint(validatedBy = EmailValidator.class) 12 | @Target({ ElementType.TYPE, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface ValidEmail { 16 | String message() default "Invalid email"; 17 | 18 | Class[] groups() default {}; 19 | 20 | Class[] payload() default {}; 21 | } 22 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:11-jdk-slim 2 | 3 | VOLUME /tmp 4 | ADD /social-network.jar app.jar 5 | RUN sh -c 'touch /app' 6 | EXPOSE 8080 7 | ENTRYPOINT ["java","-jar","/app.jar"] -------------------------------------------------------------------------------- /docker/mysql docker commands.md: -------------------------------------------------------------------------------- 1 | # Command for 'qa' profile to check locally connection to mysql 2 | 3 | ## Create docker image and start mysql container 4 | docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=password -d mysql 5 | 6 | ## Connect to mysql container 7 | docker exec -it mysql bash -l 8 | mysql -uroot -ppassword 9 | 10 | ## Stop container 11 | docker stop mysql 12 | -------------------------------------------------------------------------------- /docker/social network docker commands.md: -------------------------------------------------------------------------------- 1 | # Commands for building docker image for Social Network Project 2 | 3 | ## Go to target folder 4 | {root}\web.social.network\target 5 | 6 | ## Create container image 7 | docker build -t social-network -f ../../notForBuild/Dockerfile . 8 | 9 | ## Start container('docker' profile will be used) 10 | docker run -d --name=social-network --link mysql:mysql -p 8080:8080 -e "SPRING_PROFILES_ACTIVE=docker" social-network 11 | 12 | ## Stop container 13 | docker stop social-network -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | pom 6 | 7 | 8 | web.social.network 9 | data.social.network 10 | 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.1.7.RELEASE 16 | 17 | 18 | 19 | info.cheremisin 20 | social-network 21 | 1.0.0 22 | social-network 23 | Social Network using Spring Boot 24 | 25 | 26 | 11 27 | UTF-8 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/10.png -------------------------------------------------------------------------------- /screenshots/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/11.png -------------------------------------------------------------------------------- /screenshots/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/12.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/5.png -------------------------------------------------------------------------------- /screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/6.png -------------------------------------------------------------------------------- /screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/7.png -------------------------------------------------------------------------------- /screenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/8.png -------------------------------------------------------------------------------- /screenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/screenshots/9.png -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | # system properties file for Heroku deploy https://devcenter.heroku.com/articles/java-support#specifying-a-java-version 2 | java.runtime.version=11 -------------------------------------------------------------------------------- /web.social.network/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | social-network 7 | info.cheremisin 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | web.social.network 13 | 14 | 15 | 16 | info.cheremisin 17 | data.social.network 18 | 1.0.0 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-security 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-thymeleaf 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-devtools 37 | runtime 38 | true 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-actuator 43 | 44 | 45 | org.projectlombok 46 | lombok 47 | true 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | org.springframework.security 56 | spring-security-test 57 | test 58 | 59 | 60 | 61 | 62 | social-network 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-maven-plugin 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/SocialNetworkApplication.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SocialNetworkApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SocialNetworkApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/config/ApplicationSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.config; 2 | 3 | import info.cheremisin.social.network.service.impl.UserDetailsServiceImpl; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 8 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 13 | 14 | @Configuration 15 | @EnableWebSecurity 16 | @RequiredArgsConstructor 17 | public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { 18 | 19 | // add a reference to our security data source 20 | private final UserDetailsServiceImpl userDetailsService; 21 | private final CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler; 22 | private final BCryptPasswordEncoder bCryptPasswordEncoder; 23 | 24 | @Override 25 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 26 | auth.authenticationProvider(authenticationProvider()); 27 | } 28 | 29 | @Override 30 | protected void configure(HttpSecurity http) throws Exception { 31 | 32 | http.authorizeRequests() 33 | .antMatchers("/*").permitAll() 34 | .antMatchers("/user/*").hasAnyRole("USER", "ADMIN") 35 | .antMatchers("/admin/*").hasRole("ADMIN") 36 | .and() 37 | .formLogin() 38 | .loginPage("/") 39 | .loginProcessingUrl("/authenticateUser") 40 | .successHandler(customAuthenticationSuccessHandler) 41 | .permitAll() 42 | .and() 43 | .logout().permitAll() 44 | .and() 45 | .exceptionHandling().accessDeniedPage("/access-denied"); 46 | 47 | // http.authorizeRequests().antMatchers("/").permitAll().and() 48 | // .authorizeRequests().antMatchers("/h2-console/**").permitAll(); 49 | // http.csrf().disable(); 50 | // http.headers().frameOptions().disable(); 51 | } 52 | 53 | //authenticationProvider bean definition 54 | @Bean 55 | public DaoAuthenticationProvider authenticationProvider() { 56 | DaoAuthenticationProvider auth = new DaoAuthenticationProvider(); 57 | auth.setUserDetailsService(userDetailsService); //set the custom user details service 58 | auth.setPasswordEncoder(bCryptPasswordEncoder); //set the password encoder - bcrypt 59 | return auth; 60 | } 61 | 62 | } 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/config/BcryptPasswordEncoderConfig.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 6 | 7 | @Configuration 8 | public class BcryptPasswordEncoderConfig { 9 | 10 | @Bean 11 | public BCryptPasswordEncoder passwordEncoder() { 12 | return new BCryptPasswordEncoder(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/config/CustomAuthenticationSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.config; 2 | 3 | import info.cheremisin.social.network.dto.UserDTO; 4 | import info.cheremisin.social.network.service.UserService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.core.Authentication; 8 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.servlet.ServletException; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import javax.servlet.http.HttpSession; 15 | import java.io.IOException; 16 | 17 | @Component 18 | @RequiredArgsConstructor 19 | public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler { 20 | 21 | private final UserService userService; 22 | 23 | @Override 24 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) 25 | throws IOException { 26 | 27 | UserDTO userDTO = userService.getUserByEmail(authentication.getName()); 28 | request.getSession().setAttribute("user", userDTO); 29 | 30 | response.sendRedirect(request.getContextPath() + "/user/profile"); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/config/LocalizationConfiguration.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.LocaleResolver; 6 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 8 | import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; 9 | import org.springframework.web.servlet.i18n.SessionLocaleResolver; 10 | 11 | import java.util.Locale; 12 | 13 | @Configuration 14 | public class LocalizationConfiguration implements WebMvcConfigurer { 15 | 16 | @Bean 17 | public LocaleResolver localeResolver() { 18 | SessionLocaleResolver slr = new SessionLocaleResolver(); 19 | slr.setDefaultLocale(new Locale("en", "EN")); 20 | return slr; 21 | } 22 | 23 | @Bean 24 | public LocaleChangeInterceptor localeChangeInterceptor() { 25 | LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); 26 | lci.setParamName("lang"); 27 | return lci; 28 | } 29 | 30 | @Override 31 | public void addInterceptors(InterceptorRegistry registry) { 32 | registry.addInterceptor(localeChangeInterceptor()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/controllers/AdminController.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.controllers; 2 | 3 | import info.cheremisin.social.network.service.UserService; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | 12 | @Controller 13 | @RequestMapping("/admin") 14 | @RequiredArgsConstructor 15 | public class AdminController { 16 | 17 | private final UserService userService; 18 | 19 | @PostMapping("/makeAdmin") 20 | public String makeAdmin(@RequestParam Long userId, HttpServletRequest request) { 21 | userService.makeUserAdmin(userId); 22 | return "redirect:/user/profile/" + userId; 23 | } 24 | 25 | @PostMapping("/block") 26 | public String blockUser(@RequestParam Long userId, HttpServletRequest request) { 27 | userService.blockUser(userId); 28 | return "redirect:/user/profile/" + userId; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/controllers/FriendsController.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.controllers; 2 | 3 | import info.cheremisin.social.network.dto.UserDTO; 4 | import info.cheremisin.social.network.service.FriendsService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.ui.Model; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | import static info.cheremisin.social.network.utils.ServerUtils.getUserFromSession; 18 | 19 | @Controller 20 | @RequestMapping("/user/friends") 21 | @RequiredArgsConstructor 22 | public class FriendsController { 23 | 24 | private final FriendsService friendsService; 25 | 26 | @GetMapping 27 | public String getAllFriends(@RequestParam(value = "search", required = false) String search, 28 | Model model, HttpServletRequest request) { 29 | UserDTO user = getUserFromSession(request); 30 | Map> friends = friendsService.getFriends(user.getId(), search); 31 | model.addAttribute("usersNotAcceptedRequests", friends.get("usersNotAcceptedRequests")); 32 | model.addAttribute("notAcceptedRequestsToUser", friends.get("notAcceptedRequestsToUser")); 33 | model.addAttribute("friendsOfUser", friends.get("friendsOfUser")); 34 | return "friends"; 35 | } 36 | 37 | @GetMapping("/{friendId}/decline") 38 | public String deleteFriendship(@PathVariable Long friendId, HttpServletRequest request) { 39 | UserDTO user = getUserFromSession(request); 40 | friendsService.deleteFriendship(user, friendId); 41 | return "redirect:/user/friends"; 42 | } 43 | 44 | @GetMapping("/{friendId}/accept") 45 | public String acceptFriendship(@PathVariable Long friendId, HttpServletRequest request) { 46 | UserDTO user = getUserFromSession(request); 47 | friendsService.acceptFriendship(user, friendId); 48 | return "redirect:/user/friends"; 49 | } 50 | 51 | @GetMapping("/{friendId}/addToFriends") 52 | public String addToFriends(@PathVariable Long friendId, HttpServletRequest request) { 53 | UserDTO user = getUserFromSession(request); 54 | friendsService.addToFriends(user, friendId); 55 | return "redirect:/user/friends"; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/controllers/IndexController.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.controllers; 2 | 3 | import info.cheremisin.social.network.dto.UserDTO; 4 | import info.cheremisin.social.network.service.UserService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.ui.Model; 8 | import org.springframework.validation.BindingResult; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.ModelAttribute; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import javax.validation.Valid; 16 | import java.io.IOException; 17 | 18 | @Controller 19 | @RequiredArgsConstructor 20 | public class IndexController { 21 | 22 | private final UserService userService; 23 | 24 | @GetMapping("/") 25 | public String indexPage(HttpServletRequest request, HttpServletResponse response, Model model) throws IOException { 26 | if (request.getSession().getAttribute("user") != null) { 27 | response.sendRedirect(request.getContextPath() + "/user/profile"); 28 | return null; 29 | } 30 | model.addAttribute("user", new UserDTO()); 31 | return "index"; 32 | } 33 | 34 | @GetMapping("/access-denied") 35 | public String accessDenied() { 36 | return "service/access-denied"; 37 | } 38 | 39 | @PostMapping("/register") 40 | public String registerUser(@Valid @ModelAttribute("user") UserDTO userDTO, BindingResult bindingResult, 41 | Model model) { 42 | if (bindingResult.hasErrors()) { 43 | return "index"; 44 | } 45 | 46 | String email = userDTO.getEmail(); 47 | UserDTO userByEmail = userService.getUserByEmail(email); 48 | if (userByEmail != null) { 49 | model.addAttribute("registrationError", true); 50 | model.addAttribute("user", userDTO); 51 | return "index"; 52 | } 53 | userService.createUser(userDTO); 54 | return "service/registration-confirmation"; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/controllers/MessagesController.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.controllers; 2 | 3 | import info.cheremisin.social.network.dto.MessageDTO; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.service.MessagesService; 6 | import info.cheremisin.social.network.service.UserService; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.ui.Model; 10 | import org.springframework.validation.BindingResult; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.validation.Valid; 15 | import java.time.LocalDateTime; 16 | import java.util.Collection; 17 | import java.util.List; 18 | 19 | import static info.cheremisin.social.network.utils.ServerUtils.getUserFromSession; 20 | 21 | @Controller 22 | @RequestMapping("/user") 23 | @RequiredArgsConstructor 24 | public class MessagesController { 25 | 26 | private final MessagesService messagesService; 27 | private final UserService userService; 28 | 29 | private void addConversationToModel(Long companionId, HttpServletRequest request, Model model) { 30 | UserDTO user = getUserFromSession(request); 31 | UserDTO companion = userService.getUserById(companionId); 32 | List messages = messagesService.findConversation(user.getId(), companionId); 33 | model.addAttribute("messages", messages); 34 | model.addAttribute("companion", companion); 35 | } 36 | 37 | @GetMapping("/messages") 38 | public String getMessages(HttpServletRequest request, Model model) { 39 | UserDTO user = getUserFromSession(request); 40 | Collection recentMessages = messagesService.findAllRecentMessages(user.getId()); 41 | model.addAttribute("recentMessages", recentMessages); 42 | return "messages"; 43 | } 44 | 45 | @GetMapping("/conversation/{companionId}") 46 | public String getConversation(@PathVariable Long companionId, HttpServletRequest request, Model model) { 47 | addConversationToModel(companionId, request, model); 48 | model.addAttribute("newMessage", new MessageDTO()); 49 | return "conversation"; 50 | } 51 | 52 | @PostMapping("/conversation/{companionId}") 53 | public String postMessage(@PathVariable Long companionId, 54 | @Valid @ModelAttribute("newMessage") MessageDTO messageDTO, BindingResult bindingResult, 55 | HttpServletRequest request, Model model) { 56 | if(bindingResult.hasErrors()) { 57 | addConversationToModel(companionId, request, model); 58 | return "conversation"; 59 | } 60 | UserDTO user = getUserFromSession(request); 61 | UserDTO companion = userService.getUserById(companionId); 62 | messageDTO.setSender(user); 63 | messageDTO.setReceiver(companion); 64 | messageDTO.setTime(LocalDateTime.now()); 65 | messagesService.postMessage(messageDTO); 66 | return "redirect:/user/conversation/" + messageDTO.getReceiver().getId(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/controllers/ProfileController.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.controllers; 2 | 3 | import info.cheremisin.social.network.dto.MessageDTO; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.service.FriendsService; 6 | import info.cheremisin.social.network.service.MessagesService; 7 | import info.cheremisin.social.network.service.UserService; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.ui.Model; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | 18 | import java.io.IOException; 19 | import java.util.Set; 20 | 21 | import static info.cheremisin.social.network.utils.ServerUtils.getUserFromSession; 22 | 23 | @Controller 24 | @RequestMapping("/user") 25 | @RequiredArgsConstructor 26 | public class ProfileController { 27 | 28 | private final UserService userService; 29 | private final FriendsService friendsService; 30 | private final MessagesService messagesService; 31 | 32 | @GetMapping("/profile") 33 | public String getProfilePage(Model model, HttpServletRequest request) { 34 | UserDTO user = getUserFromSession(request); 35 | model.addAttribute("user", user); 36 | MessageDTO recentMessage = messagesService.getRecentMessage(user.getId()); 37 | model.addAttribute("recentMessage", recentMessage); 38 | Set friends = friendsService.getAcceptedFriendshipUsers(user.getId()); 39 | model.addAttribute("friends", friends); 40 | return "profile"; 41 | } 42 | 43 | @GetMapping("/profile/{id}") 44 | public String getUserPage(@PathVariable Long id, Model model, HttpServletRequest request, HttpServletResponse response) throws IOException { 45 | UserDTO sessionUser = getUserFromSession(request); 46 | if(sessionUser.getId().equals(id)) { 47 | return "redirect:/user/profile"; 48 | } 49 | UserDTO user = userService.getUserById(id); 50 | Set friends = friendsService.getAcceptedFriendshipUsers(id); 51 | Boolean friendship = friendsService.checkFriendship(sessionUser, user); 52 | model.addAttribute("user", user); 53 | model.addAttribute("usersHaveFriendship", friendship); 54 | model.addAttribute("friends", friends); 55 | return "profile"; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/controllers/SettingsController.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.controllers; 2 | 3 | import info.cheremisin.social.network.dto.PasswordChangeDTO; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.exceptions.SocialNetworkException; 6 | import info.cheremisin.social.network.service.ImageService; 7 | import info.cheremisin.social.network.service.UserService; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.ui.Model; 14 | import org.springframework.validation.BindingResult; 15 | import org.springframework.web.bind.annotation.GetMapping; 16 | import org.springframework.web.bind.annotation.ModelAttribute; 17 | import org.springframework.web.bind.annotation.PostMapping; 18 | import org.springframework.web.bind.annotation.RequestMapping; 19 | import org.springframework.web.multipart.MultipartFile; 20 | import org.springframework.web.multipart.MultipartHttpServletRequest; 21 | 22 | import javax.servlet.http.HttpServletRequest; 23 | import javax.servlet.http.HttpServletResponse; 24 | import javax.validation.Valid; 25 | import java.io.IOException; 26 | import java.util.Set; 27 | 28 | import static info.cheremisin.social.network.utils.ServerUtils.getUserFromSession; 29 | 30 | @Controller 31 | @RequestMapping("/user") 32 | @RequiredArgsConstructor 33 | public class SettingsController { 34 | 35 | private final UserService userService; 36 | private final BCryptPasswordEncoder bCryptPasswordEncoder; 37 | private final ImageService imageService; 38 | 39 | @Value("#{'${allowed.file.types}'.split(',')}") 40 | private Set allowedExtensions; 41 | 42 | @GetMapping("/settings") 43 | public String getSettingsPage(Model model) { 44 | model.addAttribute("passwordChangeDTO", new PasswordChangeDTO()); 45 | return "settings"; 46 | } 47 | 48 | @PostMapping("/settings") 49 | public String updateSettings(HttpServletRequest request, @ModelAttribute("user") UserDTO user) { 50 | userService.updateUser(user); 51 | 52 | request.getSession().setAttribute("user", userService.getUserByEmail(user.getEmail())); 53 | return "redirect:/user/settings"; 54 | } 55 | 56 | @PostMapping("/updatePassword") 57 | public String updatePassword(@Valid @ModelAttribute("passwordChangeDTO") PasswordChangeDTO passwordChangeDTO, 58 | BindingResult bindingResult, HttpServletRequest request, Model model) { 59 | if(bindingResult.hasErrors()) { 60 | return "settings"; 61 | } 62 | UserDTO user = getUserFromSession(request); 63 | String password = user.getPassword(); 64 | boolean passwordsMatch = bCryptPasswordEncoder.matches(passwordChangeDTO.getOldPassword(), password); 65 | if(!passwordsMatch) { 66 | model.addAttribute("passwordError", "Passwords doesn't match"); 67 | } else { 68 | userService.updatePassword(passwordChangeDTO.getPassword(), user.getId()); 69 | request.getSession().setAttribute("user", userService.getUserByEmail(user.getEmail())); 70 | } 71 | model.addAttribute("passwordChangeDTO", new PasswordChangeDTO()); 72 | return "settings"; 73 | } 74 | 75 | @PostMapping("/uploadImage") 76 | public String uploadImage(MultipartHttpServletRequest request) throws IOException { 77 | MultipartFile multipartFile = request.getFile("imagefile"); 78 | String contentType = multipartFile.getContentType(); 79 | if(!allowedExtensions.contains(contentType)) { 80 | throw new SocialNetworkException("File extension is not supported"); 81 | } 82 | 83 | UserDTO user = getUserFromSession(request); 84 | 85 | String newFileName = imageService.updateProfileImage(user, multipartFile); 86 | userService.updateUserImage(user, newFileName); 87 | return "redirect:/user/settings"; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/controllers/UsersController.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.controllers; 2 | 3 | import info.cheremisin.social.network.dto.PageDTO; 4 | import info.cheremisin.social.network.dto.UserDTO; 5 | import info.cheremisin.social.network.service.UserService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.data.domain.PageRequest; 10 | import org.springframework.data.domain.Sort; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.ui.Model; 13 | import org.springframework.util.StringUtils; 14 | import org.springframework.web.bind.annotation.GetMapping; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | 18 | import javax.servlet.http.HttpServletRequest; 19 | 20 | import static info.cheremisin.social.network.utils.ServerUtils.getUserFromSession; 21 | 22 | /** 23 | * Created by Dmitrii on 03.10.2019. 24 | */ 25 | @Controller 26 | @RequestMapping("/user") 27 | @RequiredArgsConstructor 28 | public class UsersController { 29 | 30 | @Value("${default.page.size}") 31 | private Integer defaultPageSize; 32 | 33 | private final UserService userService; 34 | 35 | @GetMapping("/users") 36 | public String getUserList(HttpServletRequest request, 37 | @RequestParam(value = "search", required = false) String search, 38 | @RequestParam(value = "page", required = false) Integer page, 39 | Model model) { 40 | UserDTO user = getUserFromSession(request); 41 | if (page == null) 42 | page = 0; 43 | 44 | PageRequest pageRequest = PageRequest.of(page, defaultPageSize, Sort.by("lastName").and(Sort.by("firstName"))); 45 | PageDTO allPageable; 46 | if (StringUtils.isEmpty(search)) { 47 | allPageable = userService.findAllPageable(user.getId(), pageRequest); 48 | } else { 49 | allPageable = userService.findAllWithSearch(user.getId(), search, pageRequest); 50 | } 51 | model.addAttribute("page", allPageable); 52 | model.addAttribute("search", search); 53 | return "users"; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/exceptionHandling/ResponseExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.exceptionHandling; 2 | 3 | import info.cheremisin.social.network.exceptions.SocialNetworkException; 4 | import info.cheremisin.social.network.exceptions.UserNotFoundException; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.web.bind.annotation.ControllerAdvice; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.servlet.ModelAndView; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | 12 | @Slf4j 13 | @ControllerAdvice 14 | public class ResponseExceptionHandler { 15 | 16 | @ExceptionHandler(UserNotFoundException.class) 17 | public ModelAndView handleUserNotFoundException(HttpServletRequest request, UserNotFoundException e) { 18 | log.error(">>>>>>>>>> User Not Found Exception"); 19 | log.error(e.getMessage(), e); 20 | 21 | ModelAndView modelAndView = new ModelAndView(); 22 | modelAndView.addObject("message", e.getMessage()); 23 | modelAndView.setViewName("service/user-not-found"); 24 | 25 | return modelAndView; 26 | } 27 | 28 | @ExceptionHandler(SocialNetworkException.class) 29 | public ModelAndView handleSocialNetworkException(HttpServletRequest request, SocialNetworkException e) { 30 | log.error(">>>>>>>>>> Social Network Exception"); 31 | log.error(e.getMessage(), e); 32 | 33 | ModelAndView modelAndView = new ModelAndView(); 34 | modelAndView.setViewName("service/something-went-wrong"); 35 | 36 | return modelAndView; 37 | } 38 | 39 | @ExceptionHandler(Exception.class) 40 | public ModelAndView handleAnyException(HttpServletRequest request, Exception e) { 41 | log.error(">>>>>>>>>> Exception"); 42 | log.error(e.getMessage(), e); 43 | 44 | ModelAndView modelAndView = new ModelAndView(); 45 | modelAndView.setViewName("service/something-went-wrong"); 46 | 47 | return modelAndView; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/initializer/ImageInitializer.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.initializer; 2 | 3 | import info.cheremisin.social.network.service.ImageService; 4 | import lombok.RequiredArgsConstructor; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.ApplicationListener; 8 | import org.springframework.context.event.ContextRefreshedEvent; 9 | import org.springframework.core.io.Resource; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | 18 | /** 19 | * Created by Dmitrii on 06.10.2019. 20 | */ 21 | @Component 22 | @RequiredArgsConstructor 23 | public class ImageInitializer implements ApplicationListener { 24 | 25 | private final ImageService imageService; 26 | 27 | @Value("classpath:profileImages/*") 28 | private Resource[] resources; 29 | 30 | @Override 31 | public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { 32 | try { 33 | Path pathImages = imageService.getProfileImagesPath(); 34 | if(pathImages.toFile().exists()) { 35 | Files.walk(pathImages) 36 | .map(Path::toFile) 37 | .forEach(File::delete); 38 | Files.delete(pathImages); 39 | } 40 | Files.createDirectory(pathImages); 41 | 42 | for (Resource resource : resources) { 43 | InputStream inputStream = resource.getInputStream(); 44 | Files.copy(inputStream, pathImages.resolve(resource.getFilename())); 45 | } 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /web.social.network/src/main/java/info/cheremisin/social/network/utils/ServerUtils.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network.utils; 2 | 3 | import info.cheremisin.social.network.dto.UserDTO; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | 9 | import static info.cheremisin.social.network.constants.Constants.PROFILE_IMAGES; 10 | 11 | public class ServerUtils { 12 | 13 | public static UserDTO getUserFromSession(HttpServletRequest request){ 14 | return (UserDTO) request.getSession().getAttribute("user"); 15 | } 16 | 17 | public static Path getProfileImagesPath() { 18 | return Paths.get(".").resolve(PROFILE_IMAGES); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | spring.h2.console.enabled=true 2 | spring.datasource.platform=h2 -------------------------------------------------------------------------------- /web.social.network/src/main/resources/application-docker.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 2 | spring.datasource.url=jdbc:mysql://mysql:3306/spring_social_network?createDatabaseIfNotExist=true&serverTimezone=Europe/Moscow&useUnicode=true&characterEncoding=utf8 3 | spring.datasource.username=root 4 | spring.datasource.password=root 5 | spring.datasource.platform=mysql 6 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect -------------------------------------------------------------------------------- /web.social.network/src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | server.port=${PORT} 2 | 3 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 4 | spring.datasource.url=${CLEARDB_DATABASE_URL} 5 | spring.datasource.username=${DB_USERNAME} 6 | spring.datasource.password=${DB_PASSWORD} 7 | spring.datasource.platform=mysql -------------------------------------------------------------------------------- /web.social.network/src/main/resources/application-qa.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 2 | spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_social_network?createDatabaseIfNotExist=true&serverTimezone=Europe/Moscow&useUnicode=true&characterEncoding=utf8 3 | spring.datasource.username=root 4 | spring.datasource.password=password 5 | spring.datasource.platform=mysql 6 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect -------------------------------------------------------------------------------- /web.social.network/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=prod 2 | 3 | spring.jpa.show-sql=true 4 | spring.datasource.initialization-mode=always 5 | spring.jpa.hibernate.ddl-auto=create 6 | 7 | spring.servlet.multipart.max-file-size=1MB 8 | allowed.file.types=image/jpeg,image/png 9 | 10 | default.page.size=3 11 | 12 | spring.resources.static-locations=classpath:/static/,file:./profileImages/ 13 | 14 | management.endpoints.web.base-path=/ 15 | management.endpoints.web.exposure.include=* 16 | management.endpoints.web.exposure.exclude=auditevents,beans,caches,conditions,scheduledtasks,mappings,metrics 17 | management.endpoint.health.show-details=always 18 | logging.file=logs/application.log -------------------------------------------------------------------------------- /web.social.network/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO role (id, name) 2 | VALUES 3 | (1, 'ROLE_USER'), 4 | (2, 'ROLE_ADMIN'); 5 | 6 | INSERT INTO user (id, username, password, first_name, last_name, date_of_birth, sex, phone, image) 7 | VALUES 8 | (1, 'tyrion@lannister.ru', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Tyrion', 'Lannister', '1987-09-28', 1, '+7 999 999 99 99', '1.jpg'), 9 | (2, 'cersei@lannister.ru', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Cersei', 'Lannister', '1978-09-28', 2, '+7 888 999 99 99', '2.png'), 10 | (3, 'jaime@lannister.ru', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Jaime', 'Lannister', '1975-09-28', 1, '+7 777 999 99 99', '3.jpeg'), 11 | (4, 'stark@north.no', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Ned', 'Stark', '1965-11-15', 1, '+7 666 999 99 99', '4.jpg'), 12 | (5, 'sansa.stark@north.no', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Sansa', 'Stark', '1992-04-26', 2, '+7 555 999 99 99', '5.jpg'), 13 | (6, 'aria.stark@north.no', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Aria', 'Stark', '1996-12-15', 2, '+7 555 999 99 99', '6.jpg'), 14 | (7, 'john@north.no', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Jon', 'Snow', '1988-10-11', 1, '+7 444 999 99 99', '7.jpg'), 15 | (8, 'dayneris@targarien.ta', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Daenerys', 'Targaryen', '1981-03-17', 2, '+7 333 999 99 99', '8.jpg'), 16 | (9, 'rob@north.no', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Rob', 'Stark', '1988-10-13', 1, '+7 222 999 99 99', '9.jpg'), 17 | (10, 'tywin@lannister.la', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Tywin', 'Lannister', '1952-08-27', 1, '+7 111 999 99 99', '10.jpg'), 18 | (11, 'brienne@tarth.ta', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Brienne', 'Tarth', '1967-02-19', 2, '+7 999 888 99 99', '11.jpg'), 19 | (12, 'lord@varys.va', '$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K', 'Lord', 'Varys', '1954-12-01', 1, '+7 999 777 99 99', '12.jpeg'); 20 | 21 | INSERT INTO user_role (user_id, role_id) 22 | VALUES 23 | (1, 1), 24 | (1, 2), 25 | (2, 1), 26 | (3, 1), 27 | (4, 1), 28 | (5, 1), 29 | (6, 1), 30 | (7, 1), 31 | (8, 1), 32 | (9, 1), 33 | (10, 1), 34 | (11, 1), 35 | (12, 1); 36 | 37 | INSERT INTO friendship (id, user_sender, user_receiver, accepted) 38 | VALUES 39 | (null, 1, 2, true), 40 | (null, 1, 3, true), 41 | (null, 2, 3, true), 42 | (null, 4, 5, true), 43 | (null, 4, 6, true), 44 | (null, 5, 1, false), 45 | (null, 6, 1, false), 46 | (null, 1, 10, false), 47 | (null, 1, 8, false); 48 | 49 | INSERT INTO messages (id, time, sender, receiver, message) 50 | VALUES 51 | (null, '2018-07-09 10:11:12', 1, 2, 'Привет! Как дела?'), 52 | (null, '2018-07-10 10:11:12', 2, 1, 'Привет! Спасибо, хорошо=)'), 53 | (null, '2018-07-11 10:11:12', 3, 1, 'Привет! Отец недоволен тобой! Он хочет тебя казнить'), 54 | (null, '2018-07-11 10:12:34', 1, 3, 'Что мне делать? Я не хочу умирать! У меня есть арбалет!'), 55 | (null, '2018-07-12 10:11:12', 3, 2, 'Привет! Я помогу тебе с Вестерросом! Ведь Lannisterы всегда платят свои долги=)!'), 56 | (null, '2018-07-13 10:11:12', 1, 2, 'Чего делаешь?'), 57 | (null, '2018-07-14 10:11:12', 2, 1, 'Вестеррос захватываю! Ах-ха-хаа-хааа'); -------------------------------------------------------------------------------- /web.social.network/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n 11 | 12 | logs/application.log 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/messages.properties: -------------------------------------------------------------------------------- 1 | menu.friends=Friends 2 | menu.messages=Messages 3 | menu.profile=Profile 4 | menu.settings=Settings 5 | menu.users=Users 6 | header=Social Network 7 | login=Login 8 | logout=Logout 9 | russian=Ru 10 | english=En 11 | footer.copyright=All rights reserved 12 | index.intro=Social network facilities 13 | index.registration=Registration 14 | index.authorization=Authorization 15 | index.profile=Profile view and change 16 | index.user.search=User search 17 | index.friends=Friends 18 | index.messages=Messages 19 | index.join.today=Join social network and become a member with full capabilities! 20 | index.register.today=Register today! 21 | email=Email 22 | index.password=Password 23 | index.confirm.password=Confirm Password 24 | index.register=Register 25 | conversation.with=Conversation with 26 | conversation.new=New message 27 | friends.requests=Friends requests 28 | message=Message 29 | friends.no.requests=No requests yet... 30 | friends.your.requests=Your requests 31 | friends.your=Your friends 32 | friends.no.friends=No friends yet... 33 | friends.remove=Remove 34 | friends.accept=Accept 35 | friends.decline=Decline 36 | messages.messages=Messages 37 | messages.no.messages=No messages yet... 38 | profile.add.to.friends=Add to friends 39 | profile.send.message=Send message 40 | profile.info=Profile 41 | firstName=First name 42 | lastName=Last name 43 | birth.date=Birth date 44 | gender=Gender 45 | phone=Phone 46 | profile.admin.menu=Admin menu 47 | profile.make.admin=Make Admin 48 | profile.make.usual=Make Usual User 49 | profile.unblock=Unblock 50 | profile.block=Block 51 | profile.recent.message=Recent Message 52 | profile.no.message=No messages found 53 | profile.friends=Friends 54 | profile.no.friends=No friends yet... 55 | conversation.send=Send 56 | menu.user.menu=Menu 57 | gender.male=Male 58 | gender.female=Female 59 | settings.avatar=Your avatar 60 | settings.drop.image=Drop your image here 61 | settings.update.avatar=Update avatar 62 | settings.change.profile=Change profile 63 | settings.change.password=Change password 64 | settings.old.password=Old Password 65 | settings.new.password=New Password 66 | settings.repeat.password=Repeat Password 67 | settings.your.profile=Your profile 68 | users.header=Users 69 | users.no.users=No users yet... 70 | blocked.h1=Hello, your account is blocked! 71 | blocked.p1=It seems that you have made something wrong, so we had to block your account. 72 | blocked.p2=Please, keep calm and breeth. If you are certainly sure that there is a mistake than contact our Technical support. 73 | to.main.page=Go back to main page 74 | error.header=Hi, please don't worry! 75 | error.p=Something went wrong and we are already aware of that. 76 | previous=Previous 77 | next=Next 78 | to=to 79 | provide.email=Please, provide valid email 80 | provide.password=Please, use all characters: letters and numbers at least once, minimum 6 symbols 81 | provide.pas.conf=Passwords don't match -------------------------------------------------------------------------------- /web.social.network/src/main/resources/messages_ru.properties: -------------------------------------------------------------------------------- 1 | menu.profile=\u041F\u0440\u043E\u0444\u0438\u043B\u044C 2 | menu.messages=\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F 3 | menu.friends=\u0414\u0440\u0443\u0437\u044C\u044F 4 | menu.users=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 5 | menu.settings=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 6 | header=\u0421\u043E\u0446\u0438\u0430\u043B\u044C\u043D\u0430\u044F \u0441\u0435\u0442\u044C 7 | login=\u0412\u0445\u043E\u0434 8 | logout=\u0412\u044B\u0445\u043E\u0434 9 | russian=\u0420\u0443 10 | english=\u0410\u043D 11 | footer.copyright=\u0412\u0441\u0435 \u043F\u0440\u0430\u0432\u0430 \u0437\u0430\u0449\u0438\u0449\u0435\u043D\u044B 12 | index.register=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C\u0441\u044F 13 | index.confirm.password=\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C 14 | index.password=\u041F\u0430\u0440\u043E\u043B\u044C 15 | email=\u042D\u043B. \u043F\u043E\u0447\u0442\u0430 16 | index.register.today=\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0439\u0441\u044F \u0441\u0435\u0433\u043E\u0434\u043D\u044F! 17 | index.join.today=\u041F\u0440\u0438\u0441\u043E\u0435\u0434\u0438\u043D\u044F\u0439\u0441\u044F \u043A \u0441\u043E\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0439 \u0441\u0435\u0442\u0438 \u0438 \u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C \u043F\u043E\u043B\u043D\u043E\u043F\u0440\u0430\u0432\u043D\u044B\u043C \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u043E\u043C! 18 | index.messages=\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F 19 | index.friends=\u0414\u0440\u0443\u0437\u044C\u044F 20 | index.user.search=\u041F\u043E\u0438\u0441\u043A \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 21 | index.profile=\u041F\u0440\u043E\u0441\u043C\u043E\u0442\u0440 \u0438 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435 \u043F\u0440\u043E\u0444\u0438\u043B\u044F 22 | index.authorization=\u0410\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u044F 23 | index.registration=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F 24 | index.intro=\u0412\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u0438 \u0441\u043E\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0439 \u0441\u0435\u0442\u0438 25 | conversation.new=\u041D\u043E\u0432\u043E\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 26 | conversation.with=\u041F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0430 \u0441 27 | friends.requests=\u0417\u0430\u043F\u0440\u043E\u0441\u044B \u0434\u0440\u0443\u0436\u0431\u044B 28 | message=\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 29 | friends.no.requests=\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 30 | friends.your.requests=\u0412\u0430\u0448\u0438 \u0437\u0430\u043F\u0440\u043E\u0441\u044B 31 | friends.your=\u0412\u0430\u0448\u0438 \u0434\u0440\u0443\u0437\u044C\u044F 32 | friends.no.friends=\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0434\u0440\u0443\u0437\u0435\u0439 33 | friends.remove=\u0423\u0434\u0430\u043B\u0438\u0442\u044C 34 | friends.accept=\u041F\u0440\u0438\u043D\u044F\u0442\u044C 35 | friends.decline=\u041E\u0442\u043A\u043B\u043E\u043D\u0438\u0442\u044C 36 | messages.no.messages=\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 37 | messages.messages=\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F 38 | profile.add.to.friends=\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u0434\u0440\u0443\u0437\u044C\u044F 39 | profile.send.message=\u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 40 | profile.info=\u041F\u0440\u043E\u0444\u0438\u043B\u044C 41 | firstName=\u0418\u043C\u044F 42 | lastName=\u0424\u0430\u043C\u0438\u043B\u0438\u044F 43 | birth.date=\u0414\u0430\u0442\u0430 \u0440\u043E\u0436\u0434\u0435\u043D\u0438\u044F 44 | gender=\u041F\u043E\u043B 45 | phone=\u0422\u0435\u043B\u0435\u0444\u043E\u043D 46 | profile.admin.menu=\u041C\u0435\u043D\u044E \u0430\u0434\u043C\u0438\u043D\u0430 47 | profile.make.admin=\u0421\u0434\u0435\u043B\u0430\u0442\u044C \u0430\u0434\u043C\u0438\u043D\u043E\u043C 48 | profile.make.usual=\u0421\u0434\u0435\u043B\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u043C 49 | profile.unblock=\u0420\u0430\u0437\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C 50 | profile.block=\u0411\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C 51 | profile.recent.message=\u041D\u0435\u0434\u0430\u0432\u043D\u0438\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F 52 | profile.no.message=\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 53 | profile.friends=\u0414\u0440\u0443\u0437\u044C\u044F 54 | profile.no.friends=\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0434\u0440\u0443\u0437\u0435\u0439 55 | conversation.send=\u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C 56 | menu.user.menu=\u041C\u0435\u043D\u044E 57 | gender.male=\u041C\u0443\u0436\u0441\u043A\u043E\u0439 58 | gender.female=\u0416\u0435\u043D\u0441\u043A\u0438\u0439 59 | settings.avatar=\u0412\u0430\u0448 \u0430\u0432\u0430\u0442\u0430\u0440 60 | settings.change.password=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C 61 | settings.change.profile=\u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u044C 62 | settings.drop.image=\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0444\u043E\u0442\u043E \u0441\u044E\u0434\u0430 63 | settings.new.password=\u041D\u043E\u0432\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C 64 | settings.old.password=\u0421\u0442\u0430\u0440\u044B\u0439 \u043F\u0430\u0440\u043E\u043B\u044C 65 | settings.repeat.password=\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043F\u0430\u0440\u043E\u043B\u044C 66 | settings.update.avatar=\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0430\u0432\u0430\u0442\u0430\u0440 67 | settings.your.profile=\u0412\u0430\u0448 \u043F\u0440\u043E\u0444\u0438\u043B\u044C 68 | users.no.users=\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0435\u0439 69 | users.header=\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u0438 70 | blocked.h1=\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435, \u0412\u0430\u0448 \u0430\u043A\u043A\u0430\u0443\u043D\u0442 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D! 71 | blocked.p1=\u0414\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C, \u0412\u044B \u0441\u0434\u0435\u043B\u0430\u043B\u0438 \u0447\u0442\u043E-\u0442\u043E \u043D\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u043E, \u0438 \u043D\u0430\u043C \u043F\u0440\u0438\u0448\u043B\u043E\u0441\u044C \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0412\u0430\u0448 \u0430\u043A\u043A\u0430\u0443\u043D\u0442.\ 72 | 73 | blocked.p2=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0439\u0442\u0435 \u0441\u043F\u043E\u043A\u043E\u0439\u0441\u0442\u0432\u0438\u0435 \u0438 \u0434\u044B\u0448\u0438\u0442\u0435. \u0415\u0441\u043B\u0438 \u0412\u044B \u0442\u043E\u0447\u043D\u043E \u0443\u0432\u0435\u0440\u0435\u043D\u044B, \u0447\u0442\u043E \u043F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0430, \u0442\u043E \u043E\u0431\u0440\u0430\u0442\u0438\u0442\u0435\u0441\u044C \u0432 \u0422\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0443\u044E \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443. 74 | to.main.page=\u041F\u0435\u0440\u0435\u0439\u0442\u0438 \u043D\u0430 \u0433\u043B\u0430\u0432\u043D\u0443\u044E \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0443 75 | error.header=\u041F\u0440\u0438\u0432\u0435\u0442, \u043F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u043D\u0435 \u0431\u0435\u0441\u043F\u043E\u043A\u043E\u0439\u0441\u044F! 76 | error.p=\u0427\u0442\u043E-\u0442\u043E \u043F\u043E\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A, \u0438 \u043C\u044B \u0443\u0436\u0435 \u0432 \u043A\u0443\u0440\u0441\u0435. 77 | previous=\u041D\u0430\u0437\u0430\u0434 78 | next=\u0412\u043F\u0435\u0440\u0435\u0434 79 | to=\u043A 80 | provide.pas.conf=\u041F\u0430\u0440\u043E\u043B\u0438 \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u044E\u0442 81 | provide.password=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0432\u0441\u0435 \u0441\u0438\u043C\u0432\u043E\u043B\u044B: \u0446\u0438\u0444\u0440\u044B \u0438 \u0431\u0443\u043A\u0432\u044B \u0445\u043E\u0442\u044F \u0431\u044B \u0440\u0430\u0437, \u043C\u0438\u043D\u0438\u043C\u0443\u043C 6 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432 82 | provide.email=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u0443\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u0443\u044E \u044D\u043B. \u043F\u043E\u0447\u0442\u0443 83 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/1.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/10.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/11.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/12.jpeg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/2.png -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/3.jpeg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/4.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/5.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/6.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/7.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/8.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/profileImages/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/profileImages/9.jpg -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .navbar-custom { 2 | background-color:#2b50c0; 3 | color:#ffffff; 4 | border-radius:0; 5 | } 6 | .navbar-custom .navbar-brand { 7 | color: #ecf0f1; 8 | } 9 | .jumbotron { 10 | background-color: #fff; 11 | } 12 | .icon-bar { 13 | background-color: white; 14 | } 15 | .social-img { 16 | width: 200px; 17 | height:200px; 18 | } 19 | .social-img-profile { 20 | width: 200px; 21 | height:200px; 22 | float: left; 23 | } 24 | .sidebar-header { 25 | text-align: center; 26 | margin-top: 20px; 27 | } 28 | .panel{ 29 | margin-left: 1px; 30 | margin-right: 1px; 31 | width:100%; 32 | } 33 | .panel-right { 34 | float: right; 35 | width: 70%; 36 | } 37 | .profile-block::after { 38 | content: ""; 39 | display: table; 40 | clear: both; 41 | } 42 | .margin-top { 43 | margin-top: 20px; 44 | } 45 | .friends-top { 46 | margin-top: 30px; 47 | } 48 | .profile-friend { 49 | margin-right:10px; 50 | } 51 | .image-upload { 52 | width: 200px; 53 | height: 200px; 54 | } 55 | .image-drop { 56 | width: 208px; 57 | height: 208px; 58 | float: left; 59 | text-align: center; 60 | line-height: 208px; 61 | } 62 | .btn-vertical { 63 | padding-top: 70px; 64 | float: left; 65 | } 66 | .btn-left { 67 | margin-left: 70px; 68 | } 69 | .border-dotted { 70 | border: 1px dotted black; 71 | } 72 | .border-dotted-big { 73 | border: 2px dotted black; 74 | } 75 | .drop-text { 76 | font-size: 18px; 77 | } 78 | .message-left { 79 | float: left; 80 | margin-right:10px; 81 | } 82 | .img-message { 83 | width: 50px; 84 | height:50px; 85 | margin-right: 5px; 86 | } 87 | .image-right { 88 | float: right; 89 | } 90 | .image-left { 91 | float: left; 92 | margin-right: 10px; 93 | text-align: center; 94 | } 95 | .stylish-input-group .input-group-addon{ 96 | background:transparent; 97 | } 98 | .stylish-input-group .form-control{ 99 | border-right:0; 100 | box-shadow:0 0 0; 101 | border-color:#ccc; 102 | } 103 | .stylish-input-group button{ 104 | border:0; 105 | background:transparent; 106 | } 107 | .table > tbody > tr > td { 108 | border: none; 109 | } 110 | .friends > tbody > tr > td { 111 | width: 25%; 112 | vertical-align: middle; 113 | } 114 | .profile-info { 115 | width: 70%; 116 | } 117 | .users { 118 | width: 60%; 119 | } 120 | .users > tbody > tr > td { 121 | vertical-align: middle; 122 | } 123 | .lang.langSelected { 124 | color: lightgreen; 125 | } -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/css/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2016-09-14 2 | * http://jqueryui.com 3 | * Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css 4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6 5 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 6 | 7 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;font-size:100%}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-text{display:block;margin-right:20px;overflow:hidden;text-overflow:ellipsis}.ui-selectmenu-button.ui-button{text-align:left;white-space:nowrap;width:14em}.ui-selectmenu-icon.ui-icon{float:right;margin-top:0}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:.222em 0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:2em}.ui-spinner-button{width:1.6em;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top-style:none;border-bottom-style:none;border-right-style:none}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.003;filter:Alpha(Opacity=.3)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmcheremisin/SpringBootSocialNetwork/65523e00a80103547df489f99064461202b415dc/web.social.network/src/main/resources/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/images/noname.svg: -------------------------------------------------------------------------------- 1 | 200x200 -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/js/bootstrap-notify.min.js: -------------------------------------------------------------------------------- 1 | /* Project: Bootstrap Growl = v3.1.3 | Description: Turns standard Bootstrap alerts into "Growl-like" notifications. | Author: Mouse0270 aka Robert McIntosh | License: MIT License | Website: https://github.com/mouse0270/bootstrap-growl */ 2 | !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t("object"==typeof exports?require("jquery"):jQuery)}(function(t){function e(e,i,n){var i={content:{message:"object"==typeof i?i.message:i,title:i.title?i.title:"",icon:i.icon?i.icon:"",url:i.url?i.url:"#",target:i.target?i.target:"-"}};n=t.extend(!0,{},i,n),this.settings=t.extend(!0,{},s,n),this._defaults=s,"-"==this.settings.content.target&&(this.settings.content.target=this.settings.url_target),this.animations={start:"webkitAnimationStart oanimationstart MSAnimationStart animationstart",end:"webkitAnimationEnd oanimationend MSAnimationEnd animationend"},"number"==typeof this.settings.offset&&(this.settings.offset={x:this.settings.offset,y:this.settings.offset}),this.init()}var s={element:"body",position:null,type:"info",allow_dismiss:!0,newest_on_top:!1,showProgressbar:!1,placement:{from:"top",align:"right"},offset:20,spacing:10,z_index:1031,delay:5e3,timer:1e3,url_target:"_blank",mouse_over:null,animate:{enter:"animated fadeInDown",exit:"animated fadeOutUp"},onShow:null,onShown:null,onClose:null,onClosed:null,icon_type:"class",template:''};String.format=function(){for(var t=arguments[0],e=1;e .progress-bar').removeClass("progress-bar-"+t.settings.type),t.settings.type=i[e],this.$ele.addClass("alert-"+i[e]).find('[data-notify="progressbar"] > .progress-bar').addClass("progress-bar-"+i[e]);break;case"icon":var n=this.$ele.find('[data-notify="icon"]');"class"==t.settings.icon_type.toLowerCase()?n.removeClass(t.settings.content.icon).addClass(i[e]):(n.is("img")||n.find("img"),n.attr("src",i[e]));break;case"progress":var a=t.settings.delay-t.settings.delay*(i[e]/100);this.$ele.data("notify-delay",a),this.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",i[e]).css("width",i[e]+"%");break;case"url":this.$ele.find('[data-notify="url"]').attr("href",i[e]);break;case"target":this.$ele.find('[data-notify="url"]').attr("target",i[e]);break;default:this.$ele.find('[data-notify="'+e+'"]').html(i[e])}var o=this.$ele.outerHeight()+parseInt(t.settings.spacing)+parseInt(t.settings.offset.y);t.reposition(o)},close:function(){t.close()}}},buildNotify:function(){var e=this.settings.content;this.$ele=t(String.format(this.settings.template,this.settings.type,e.title,e.message,e.url,e.target)),this.$ele.attr("data-notify-position",this.settings.placement.from+"-"+this.settings.placement.align),this.settings.allow_dismiss||this.$ele.find('[data-notify="dismiss"]').css("display","none"),(this.settings.delay<=0&&!this.settings.showProgressbar||!this.settings.showProgressbar)&&this.$ele.find('[data-notify="progressbar"]').remove()},setIcon:function(){"class"==this.settings.icon_type.toLowerCase()?this.$ele.find('[data-notify="icon"]').addClass(this.settings.content.icon):this.$ele.find('[data-notify="icon"]').is("img")?this.$ele.find('[data-notify="icon"]').attr("src",this.settings.content.icon):this.$ele.find('[data-notify="icon"]').append('Notify Icon')},styleURL:function(){this.$ele.find('[data-notify="url"]').css({backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)",height:"100%",left:"0px",position:"absolute",top:"0px",width:"100%",zIndex:this.settings.z_index+1}),this.$ele.find('[data-notify="dismiss"]').css({position:"absolute",right:"10px",top:"5px",zIndex:this.settings.z_index+2})},placement:function(){var e=this,s=this.settings.offset.y,i={display:"inline-block",margin:"0px auto",position:this.settings.position?this.settings.position:"body"===this.settings.element?"fixed":"absolute",transition:"all .5s ease-in-out",zIndex:this.settings.z_index},n=!1,a=this.settings;switch(t('[data-notify-position="'+this.settings.placement.from+"-"+this.settings.placement.align+'"]:not([data-closing="true"])').each(function(){return s=Math.max(s,parseInt(t(this).css(a.placement.from))+parseInt(t(this).outerHeight())+parseInt(a.spacing))}),1==this.settings.newest_on_top&&(s=this.settings.offset.y),i[this.settings.placement.from]=s+"px",this.settings.placement.align){case"left":case"right":i[this.settings.placement.align]=this.settings.offset.x+"px";break;case"center":i.left=0,i.right=0}this.$ele.css(i).addClass(this.settings.animate.enter),t.each(Array("webkit","moz","o","ms",""),function(t,s){e.$ele[0].style[s+"AnimationIterationCount"]=1}),t(this.settings.element).append(this.$ele),1==this.settings.newest_on_top&&(s=parseInt(s)+parseInt(this.settings.spacing)+this.$ele.outerHeight(),this.reposition(s)),t.isFunction(e.settings.onShow)&&e.settings.onShow.call(this.$ele),this.$ele.one(this.animations.start,function(){n=!0}).one(this.animations.end,function(){t.isFunction(e.settings.onShown)&&e.settings.onShown.call(this)}),setTimeout(function(){n||t.isFunction(e.settings.onShown)&&e.settings.onShown.call(this)},600)},bind:function(){var e=this;if(this.$ele.find('[data-notify="dismiss"]').on("click",function(){e.close()}),this.$ele.mouseover(function(){t(this).data("data-hover","true")}).mouseout(function(){t(this).data("data-hover","false")}),this.$ele.data("data-hover","false"),this.settings.delay>0){e.$ele.data("notify-delay",e.settings.delay);var s=setInterval(function(){var t=parseInt(e.$ele.data("notify-delay"))-e.settings.timer;if("false"===e.$ele.data("data-hover")&&"pause"==e.settings.mouse_over||"pause"!=e.settings.mouse_over){var i=(e.settings.delay-t)/e.settings.delay*100;e.$ele.data("notify-delay",t),e.$ele.find('[data-notify="progressbar"] > div').attr("aria-valuenow",i).css("width",i+"%")}t<=-e.settings.timer&&(clearInterval(s),e.close())},e.settings.timer)}},close:function(){var e=this,s=parseInt(this.$ele.css(this.settings.placement.from)),i=!1;this.$ele.data("closing","true").addClass(this.settings.animate.exit),e.reposition(s),t.isFunction(e.settings.onClose)&&e.settings.onClose.call(this.$ele),this.$ele.one(this.animations.start,function(){i=!0}).one(this.animations.end,function(){t(this).remove(),t.isFunction(e.settings.onClosed)&&e.settings.onClosed.call(this)}),setTimeout(function(){i||(e.$ele.remove(),e.settings.onClosed&&e.settings.onClosed(e.$ele))},600)},reposition:function(e){var s=this,i='[data-notify-position="'+this.settings.placement.from+"-"+this.settings.placement.align+'"]:not([data-closing="true"])',n=this.$ele.nextAll(i);1==this.settings.newest_on_top&&(n=this.$ele.prevAll(i)),n.each(function(){t(this).css(s.settings.placement.from,e),e=parseInt(e)+parseInt(s.settings.spacing)+t(this).outerHeight()})}}),t.notify=function(t,s){var i=new e(this,t,s);return i.notify},t.notifyDefaults=function(e){return s=t.extend(!0,{},s,e)},t.notifyClose=function(e){"undefined"==typeof e||"all"==e?t("[data-notify]").find('[data-notify="dismiss"]').trigger("click"):t('[data-notify-position="'+e+'"]').find('[data-notify="dismiss"]').trigger("click")}}); -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/js/notification.js: -------------------------------------------------------------------------------- 1 | var host = window.location.host; 2 | var path = window.location.pathname; 3 | var webCtx = path.substring(0, path.indexOf('/', 1)); 4 | var endPointURL = "ws://" + host + webCtx + "/push"; 5 | 6 | var chatClient = null; 7 | 8 | function connect() { 9 | chatClient = new WebSocket(endPointURL); 10 | chatClient.onmessage = function (event) { 11 | $.notify({ 12 | message: event.data 13 | },{ 14 | type: 'info' 15 | }); 16 | }; 17 | } 18 | 19 | function disconnect() { 20 | chatClient.close(); 21 | } 22 | 23 | function sendMessage() { 24 | var messageBody = $(".message-body").val().trim(); 25 | if(messageBody) { 26 | var companionUserId = $(".companion-user").val().trim(); 27 | var jsonObj = {"companion": companionUserId, "message": messageBody}; 28 | chatClient.send(JSON.stringify(jsonObj)); 29 | } 30 | } 31 | 32 | $(document).ready(function () { 33 | connect(); 34 | $('.submit-message').on("click", function () { 35 | sendMessage(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/js/profile.js: -------------------------------------------------------------------------------- 1 | var file; 2 | function dragAndDrop(event) { 3 | event.preventDefault(); 4 | event.stopPropagation(); 5 | var fileName = URL.createObjectURL(event.dataTransfer.files[0]); 6 | var $preview = $(".image-drop"); 7 | if($("div.image-drop img").length > 0 ) { 8 | $("div.image-drop > img").remove(); 9 | } else{ 10 | $("div.image-drop > p").remove(); 11 | } 12 | var previewImg = document.createElement("img"); 13 | previewImg.setAttribute("class", "img-circle img-thumbnail image-upload uploaded-image"); 14 | previewImg.setAttribute("src", fileName); 15 | $preview.append(previewImg); 16 | file = event.dataTransfer.files[0]; 17 | dragLeave(event); 18 | }; 19 | function dragEnter(event) { 20 | event.preventDefault(); 21 | event.stopPropagation(); 22 | var $div = $("div.image-drop"); 23 | $div.removeClass("border-dotted"); 24 | $div.addClass("border-dotted-big"); 25 | return false; 26 | }; 27 | function dragLeave(event) { 28 | event.preventDefault(); 29 | event.stopPropagation(); 30 | var $div = $("div.image-drop"); 31 | $div.removeClass("border-dotted-big"); 32 | $div.addClass("border-dotted"); 33 | return false; 34 | }; 35 | $("button.btn-left").click(function (event) { 36 | event.preventDefault(); 37 | var formData = new FormData(); 38 | console.log(file); 39 | formData.append("imagefile", file); 40 | var token = $("meta[name='_csrf']").attr("content"); 41 | $.ajax({ 42 | url: '/user/uploadImage', 43 | type: 'POST', 44 | enctype: 'multipart/form-data', 45 | headers: { "X-CSRF-TOKEN": token }, 46 | data: formData, 47 | processData: false, 48 | contentType: false, 49 | error: function () { 50 | alert("Please, try to use image smaller size"); 51 | } 52 | }).then(function () { 53 | window.location.reload(); 54 | }) 55 | }); 56 | $(function() { 57 | $.datepicker.regional['ru'] = { 58 | closeText: 'Закрыть', 59 | prevText: 'Пред', 60 | nextText: 'След', 61 | currentText: 'Сегодня', 62 | monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь', 63 | 'Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'], 64 | monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн', 65 | 'Июл','Авг','Сен','Окт','Ноя','Дек'], 66 | dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'], 67 | dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'], 68 | dayNamesMin: ['Вс','Пн','Вт','Ср','Чт','Пт','Сб'], 69 | weekHeader: 'Нед', 70 | dateFormat: 'dd.mm.yy', 71 | firstDay: 1, 72 | isRTL: false, 73 | showMonthAfterYear: false, 74 | yearSuffix: '',}; 75 | $.datepicker.setDefaults($.datepicker.regional['ru']); 76 | var options = { 77 | changeMonth: true, 78 | changeYear: true, 79 | highlightWeek: true, 80 | yearRange: "-100:+0" 81 | }; 82 | $( "#datepicker" ).datepicker(options); 83 | }); -------------------------------------------------------------------------------- /web.social.network/src/main/resources/static/js/validation.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | // registration selectors 3 | var register_email = ".register_email"; 4 | var register_password = ".register_password"; 5 | var register_password_confirm = ".register_password_confirm"; 6 | var register_submit = ".register_submit"; 7 | var error = "_error"; 8 | var register_email_error = register_email + error; 9 | var register_password_error = register_password + error; 10 | var register_password_confirm_error = register_password_confirm + error; 11 | var $register_email = $(register_email); 12 | var $register_password = $(register_password); 13 | var $register_password_confirm = $(register_password_confirm); 14 | var $register_email_error = $(register_email_error); 15 | var $register_password_error = $(register_password_error); 16 | var $register_password_confirm_error = $(register_password_confirm_error); 17 | var $register_submit = $(register_submit); 18 | 19 | // login selectors 20 | var $login_email = $(".login_email"); 21 | var $login_password = $(".login_password"); 22 | var $login_submit = $(".login_submit"); 23 | 24 | $register_email.on("change", function () { 25 | showError($register_email, checkEmail($register_email), $register_email_error, $register_submit); 26 | }); 27 | $register_password.on("change", function () { 28 | showError($register_password, checkPassword($register_password), $register_password_error, $register_submit); 29 | showError($register_password_confirm, checkPasswordConf($register_password, $register_password_confirm), $register_password_confirm_error, $register_submit); 30 | }); 31 | $register_password_confirm.on("change", function () { 32 | showError($register_password_confirm, checkPasswordConf($register_password, $register_password_confirm), $register_password_confirm_error, $register_submit); 33 | }); 34 | $register_submit.on("click", function (e) { 35 | var emailTest = checkEmail($register_email); 36 | var passwordTest = checkPassword($register_password); 37 | var passwordConfTest = checkPasswordConf($register_password, $register_password_confirm); 38 | if(!emailTest || !passwordTest || !passwordConfTest) { 39 | e.preventDefault(); 40 | showError($register_email, emailTest, $register_email_error, $register_submit); 41 | showError($register_password, passwordTest, $register_password_error, $register_submit); 42 | showError($register_password_confirm, passwordConfTest, $register_password_confirm_error, $register_submit); 43 | $register_submit.prop('disabled', true); 44 | } 45 | }); 46 | 47 | $login_email.on("change", function () { 48 | highlightField($login_email, checkEmail($login_email), $login_submit); 49 | }); 50 | $login_password.on("change", function () { 51 | highlightField($login_password, checkPassword($login_password), $login_submit); 52 | }); 53 | $login_submit.on("click", function (e) { 54 | var emailTest = checkEmail($login_email); 55 | var passwordTest = checkPassword($login_password); 56 | if(!emailTest || !passwordTest) { 57 | e.preventDefault(); 58 | highlightField($login_email, checkEmail($login_email), $login_submit); 59 | highlightField($login_password, checkPassword($login_password), $login_submit); 60 | $login_submit.prop('disabled', true); 61 | } 62 | }); 63 | 64 | }); 65 | function showError(element, result, error, submit) { 66 | submit.prop('disabled', !result); 67 | if (!result) { 68 | error.removeClass("hidden"); 69 | element.closest("div.form-group").addClass("has-error"); 70 | } else { 71 | error.addClass("hidden"); 72 | element.closest("div.form-group").removeClass("has-error"); 73 | } 74 | } 75 | function highlightField(element, result, submit) { 76 | submit.prop('disabled', !result); 77 | if (!result) { 78 | element.closest("div.form-group").addClass("has-error"); 79 | } else { 80 | element.closest("div.form-group").removeClass("has-error"); 81 | } 82 | } 83 | function checkEmail($element) { 84 | var value = $element.val(); 85 | var emailRegexp = /^[a-zA-Z0-9-_.]+@[a-zA-Z0-9-_]+\.([.]*[a-zA-Z0-9-_]+)+$/; 86 | return emailRegexp.test(value); 87 | } 88 | function checkPassword($element) { 89 | var password = $element.val(); 90 | var passwordRegexp = /(?=.*[a-zA-Z])(?=.*[0-9])(?=.{6,})/; 91 | return passwordRegexp.test(password); 92 | } 93 | 94 | function checkPasswordConf($password, $confirmation) { 95 | var conf = $confirmation.val(); 96 | var password = $password.val(); 97 | return conf === password; 98 | } -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/conversation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 |

Conversation with 17 | 18 |

19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
22.11.2018 12:25
27 |
Hello! How are you?
28 |
29 |
30 |
31 |
32 | New message 33 |
34 |
35 |
36 |
37 | 38 |
39 |
    40 |
  • 41 |
42 |
43 |
44 |
45 | 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/friends.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | 20 | 21 | 24 | 25 |
26 |
27 |
28 | 29 |
30 |

Your requests

31 | 32 | 33 | 36 | 39 | 40 | 41 | 42 |
34 | 35 | 37 | Message 38 |
43 |
44 | 45 |
46 |

Friends requests to you

47 | 48 | 49 | 52 | 55 | 58 | 61 | 62 |
50 | 51 | 53 | Accept 54 | 56 | Decline 57 | 59 | Message 60 |
63 |
64 | 65 |
66 |

Your friends

67 | 68 | 69 | 74 | 77 | 78 |
70 | 71 | 72 | Message 73 | 75 | Remove 76 |
79 |
80 |
81 |
82 |
83 | 84 |
85 | 86 | 87 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 | 11 |
12 |
13 |
14 |
15 |
16 |

Register today!

17 |
18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 |

User with such email has already been registered

26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 | 36 | 37 |
38 |
    39 |
  • 40 |
41 |
42 |
43 |
44 | 47 | 48 |
49 |
    50 |
  • 51 |
52 |
53 |
54 |
55 | 58 | 59 |
60 |
    61 |
  • 62 |
63 |
64 |
65 |
66 | 69 | 70 |
71 |
    72 |
  • 73 |
74 |
75 |
76 |
77 | 80 | 81 |
82 |
    83 |
  • 84 |
85 |
86 |
87 |
88 | 89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | 99 |
100 | 101 | 102 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/messages.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 |

Messages

17 | 18 |
19 |
20 |
21 | 22 | to   23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 | 44 | 74 | 75 | 76 | 77 | 102 | 103 |
21 |
22 | 24 | 26 |

Tyrion Lanister

27 |
28 | 29 | 30 | 33 | 36 | 37 | 38 | 41 | 42 |
31 | Friend 32 | 34 | Add to friends 35 |
39 | Message 40 |
43 |
45 |
46 |
47 |

Profile

48 |
49 |
50 |

51 | First name: 52 | Tyron 53 |

54 |

55 | First name: 56 | Lanister 57 |

58 |

59 | Birth date: 60 | 11.01.1989 61 |

62 |

63 | Gender: 64 | Male 65 | Female 66 |

67 |

68 | Phone: 69 | +7 999 999 99 99 70 |

71 |
72 |
73 |
78 |
79 |
80 |

Admin menu

81 |
82 |
83 |
84 |
85 | 86 | 89 |
90 |
91 |
92 |
93 | 94 | 97 |
98 |
99 |
100 |
101 |
104 |
105 |
106 |

Recent Message:

107 |
108 |
109 |
110 |   111 | to   112 | 113 |
114 |
115 |
116 |
117 | Hello! How are you? 118 |
119 |
120 |
121 |
122 |
123 |

Friends:

124 |
125 |
126 | 127 | 128 |   129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | 137 |
138 | 139 | 140 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/service/access-denied.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 |
11 |

Your are not allowed to see this page

12 |
13 |
14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/service/registration-confirmation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 |
11 |

Thank you for your registration!

12 |
13 |

You may now login with email and password

14 |
15 |
16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/service/something-went-wrong.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 |
11 |

Something went wrong. Please, try again later.

12 |
13 |
14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/service/user-not-found.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 |
11 |

User is not found

12 |
13 |
14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 |

Your avatar.

17 |
18 |
19 |
21 |

Drop your image here.

22 |
23 |
24 | 25 |
26 |
27 |
28 | 30 | 32 |
33 |
34 |
35 |
36 |
37 |

Your profile.

38 |
39 |
40 |
41 |
42 | 46 |
47 |
48 | 52 |
53 |
54 | 58 |
59 |
60 | 65 |
66 |
Gender: 67 | 71 | 75 |
76 |
77 | 81 |
82 |
83 | 84 |
85 | 86 |
87 |
88 |
89 |

Change password

90 |
91 |
92 |
93 |
94 | 101 |
102 |

You entered wrong old password

103 |
104 |
105 |
    106 |
  • 107 |
108 |
109 |
110 |
111 | 118 |
119 |
    120 |
  • 121 |
122 |
123 |
124 |
125 | 132 |
133 |
    134 |
  • 135 |
136 |
137 |
138 |
139 |
140 | 141 |
142 |
143 |
144 |
145 |
146 | 147 |
148 | 149 | 150 | 151 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Social Network 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 49 |
50 | 51 |
52 |
53 |

Social network facilities:

54 |
    55 |
  • Registration
  • 56 |
  • Authorization
  • 57 |
  • Profile view and change
  • 58 |
  • User search
  • 59 |
  • Friends
  • 60 |
  • Messages
  • 61 |
62 |
63 |

Join social network and become a member with full capabilities!

64 |
65 |
66 | 67 |
68 |
69 |
70 | 71 | 72 |
73 |
74 |

User menu

75 |
76 |
77 | Profile 78 | Messages 79 | Friends 80 | Users 81 | Settings 82 |
83 |
84 |
85 |
86 |

87 | 88 | User's image 90 | Sansa Stark 91 | 92 |

93 |

94 | 95 | 96 | Tyrion 97 | 98 |

99 |
100 |
101 |
102 | 103 |
104 |
105 |
106 |

© All rights reserved

107 |
108 |
109 | 110 | 111 | 112 | 113 |
114 | 115 | 116 | -------------------------------------------------------------------------------- /web.social.network/src/main/resources/templates/users.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 | 20 | 21 | 24 | 25 |
26 |
27 |
28 | 29 |
30 |

Users:

31 | 32 | 33 | 39 | 42 | 43 |
34 | 35 |
23.05.1986, 36 | Male 37 | Female 38 |
40 | Message 41 |
44 |
45 | 67 |
68 |
69 |
70 |
71 |
72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /web.social.network/src/test/java/info/cheremisin/social/network/SocialNetworkApplicationTests.java: -------------------------------------------------------------------------------- 1 | package info.cheremisin.social.network; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SocialNetworkApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------