<repo_name>emrissol18/galleryspringapi<file_sep>/src/main/java/com/gallery/util/ResponseModel.java
package com.gallery.util;


import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.http.HttpStatus;
import java.util.HashMap;
import java.util.Map;

//import lombok.ToString;
@ApiModel(value = "Response Model")
@Getter
@Setter
public class ResponseModel {

    public static final String ATTR_USERS = "users";
    public static final String ATTR_USER = "user";
    public static final String ATTR_SUBSCRIPTIONS = "subscriptions";
    public static final String ATTR_SUBSCRIBED = "subscribed";
    public static final String ATTR_ALBUMS = "albums";
    public static final String ATTR_ALBUM = "album";
    public static final String ATTR_IMAGES = "images";
    public static final String ATTR_PAGINATION = "pagination";
    public static final String ATTR_FORM_ERRORS = "formErrors";
    public static final String ATTR_UPDATES = "updates";
    public static final String ATTR_UPDATES_COUNT = "updatesCount";
    public static final String ATTR_THEME = "theme";

    @ApiModelProperty(name = "attributes to hold data")
    @ToString.Exclude
    private Map<String, Object> attributes = new HashMap<>(4, 1);

    @ApiModelProperty(name = "status code", example = "200", allowEmptyValue = true)
    @Setter(AccessLevel.NONE)
    private int statusCode;

    @ApiModelProperty(name = "status message")
    @Setter(AccessLevel.NONE)
    private String statusMessage;

    public ResponseModel() {
        defaultStatus();
    }

    public void setStatus(HttpStatus httpStatus) {
        statusCode = httpStatus.value();
        statusMessage = httpStatus.getReasonPhrase();
    }

    public ResponseModel(HttpStatus status) {
        setStatus(status);
    }

    public ResponseModel(String key, Object object) {
        defaultStatus();
        addAttribute(key, object);
    }

    /*public ResponseModel(String key, Iterable<Object> objects) {
        defaultStatus();
        addAttribute(key, objects);
    }*/

    public ResponseModel(Map<String, Object> attributes) {
        defaultStatus();
        this.attributes.putAll(attributes);
    }

    public void addAttribute(String key, Object object) {
        attributes.put(key, object);
    }

    public void addAttribute(String key, Iterable<Object> object) {
        attributes.put(key, object);
    }

    private void defaultStatus() {
        setStatus(HttpStatus.OK);
    }

}
<file_sep>/src/main/java/com/gallery/web/AlbumController.java
package com.gallery.web;


import com.gallery.model.dto.PaginationData;
import com.gallery.model.dto.album.AlbumDto;
import com.gallery.model.dto.album.AlbumDtoForm;
import com.gallery.model.entity.album.Album;
import com.gallery.model.entity.user.User;
import com.gallery.model.entity.user.UserData;
import com.gallery.service.AlbumService;
import com.gallery.service.ImageService;
import com.gallery.service.UserDataService;
import com.gallery.service.UserService;
import com.gallery.service.delayedtask.DelayedEntityRemover;
import com.gallery.util.FunctionUtil;
import com.gallery.util.MailSenderService;
import com.gallery.util.ResponseModel;
import com.gallery.util.factory.MultipartFileImageFactoryImpl;
import com.gallery.util.image.MultipartFileImage;
import com.gallery.validation.ImageExtensionValidator;
import io.swagger.annotations.*;
import org.hibernate.HibernateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Tuple;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

//@Slf4j
@RestController
@RequestMapping("/album")
public class AlbumController {

    private static Logger logger = LoggerFactory.getLogger(AlbumController.class);

    @Value("${pagination.page-items-size}")
    private int pageItemsSize;

    @PersistenceContext
    private EntityManager entityManager;

    private UserService userService;
    private AlbumService albumService;
    private ImageService imageService;
    private MultipartFileImageFactoryImpl multipartFileImageFactory;
    private ImageExtensionValidator extensionValidator;
    private MailSenderService mailSender;
    private DelayedEntityRemover<Album> albumDelayedRemover;
    private UserDataService userDataService;

    @Autowired
    public AlbumController(UserService userService,
                           AlbumService albumService,
                           ImageService imageService,
                           MultipartFileImageFactoryImpl multipartFileImageFactory,
                           ImageExtensionValidator extensionValidator,
                           MailSenderService mailSender,
                           @Qualifier("albumRemover") DelayedEntityRemover albumDelayedRemover,
                           UserDataService userDataService) {
        this.userService = userService;
        this.albumService = albumService;
        this.imageService = imageService;
        this.multipartFileImageFactory = multipartFileImageFactory;
        this.extensionValidator = extensionValidator;
        this.mailSender = mailSender;
//        this.entityServiceMulti = entityServiceMulti;
//        this.entityServiceSingle = entityServiceSingle;
        this.albumDelayedRemover = albumDelayedRemover;
        this.userDataService = userDataService;
    }

    /**
     * Fetch all albums and images
     */
    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "get albums")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "page"),
            @ApiImplicitParam(name = "offset"),
            @ApiImplicitParam(name = "limit"),
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "get albums", responseContainer = "List")
    })
    @GetMapping("/all")
    public ResponseModel albumsAll(
            @RequestParam(value = "page", defaultValue = "0", required = false) Integer page,
            @RequestParam(value = "offset", defaultValue = "0", required = false) Integer offset,
            @RequestParam(value = "limit", required = false) Integer limit) {
        logger.info("get all albums");

        long itemsTotal = albumService.countNoDeleted();
        offset = FunctionUtil.resolveOffset(offset, page, pageItemsSize);
        limit = FunctionUtil.resolveLimit(limit, pageItemsSize);

        Collection<AlbumDto> albumDtos = albumService.findAllAndConstructAlbumDto(offset, limit);

        PaginationData paginationData = new PaginationData(offset, page, itemsTotal, albumDtos.size(), pageItemsSize);
        
        ResponseModel responseModel = new ResponseModel();
        responseModel.addAttribute(ResponseModel.ATTR_ALBUMS, albumDtos);
        responseModel.addAttribute(ResponseModel.ATTR_PAGINATION, paginationData);
        return responseModel;
    }


    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "get albums by username")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "page"),
            @ApiImplicitParam(name = "offset"),
            @ApiImplicitParam(name = "limit"),
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "get albums", responseContainer = "List")
    })
    @GetMapping("/{username}/all")
    public ResponseModel albumsByUser(@ApiIgnore @AuthenticationPrincipal User principal,
                                      @ApiIgnore @PathVariable String username,
                                      @ApiParam("page") @RequestParam(value = "page", defaultValue = "0", required = false) Integer page,
                                      @ApiParam("offset") @RequestParam(value = "offset", defaultValue = "0", required = false) Integer offset,
                                      @ApiParam("limit") @RequestParam(value = "limit", required = false) Integer limit) {
        logger.info("get albums by User");

        ResponseModel responseModel = new ResponseModel();

        User user = null;
        long itemsTotal = 0;
        boolean subscribed = false;

        if (principal != null) {
            Optional<Tuple> tupleOptional =
                    Optional.ofNullable(userService.findUserTupleWithAlbumsCountAndIsSubscribed(username, principal.getId()));
            if(tupleOptional.isPresent()) {
                try {
                    user = (User) tupleOptional.get().get(0);
                    itemsTotal = (Long) tupleOptional.get().get(1);
                    subscribed = (Boolean) tupleOptional.get().get(2);
                }
                catch (IllegalArgumentException | ClassCastException e) {
                    e.printStackTrace();
                    responseModel.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
                    return responseModel;
                }
            }
        }
        else {
            user = userService.findByUsernameWithAlbums(username);
        }
        
        offset = FunctionUtil.resolveOffset(offset, page, pageItemsSize);
        limit = FunctionUtil.resolveLimit(limit, pageItemsSize);

        Collection<AlbumDto> albumDtos = albumService.findAllAndConstructAlbumDto(
                FunctionUtil.getWhereClauseEntry("user", "id", user.getId().toString()),
                offset, limit);

        PaginationData paginationData = new PaginationData(offset, page, itemsTotal, albumDtos.size(), pageItemsSize);

        responseModel.addAttribute(ResponseModel.ATTR_ALBUMS, albumDtos);
        responseModel.addAttribute(ResponseModel.ATTR_SUBSCRIBED, subscribed);
        responseModel.addAttribute(ResponseModel.ATTR_PAGINATION, paginationData);
        return responseModel;
    }

    @ResponseStatus(HttpStatus.CREATED)
    @ApiOperation(value = "create album", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "formData[cover, title, description]"),
    })
    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "album created"),
            @ApiResponse(code = 500, message = "internal server error (exception)")
    })
    @PostMapping("/create")
    public ResponseModel albumCreate(@ApiIgnore @AuthenticationPrincipal User principal,
                                     @ApiParam("formData") @ModelAttribute AlbumDtoForm albumDto) {
        ResponseModel responseModel = new ResponseModel(HttpStatus.CREATED);

        Album album = new Album(albumDto.getTitle(), albumDto.getDescription());
        album.setUser(principal);
        try {
            albumService.save(album);
            if ( ( ! albumDto.getCover().isEmpty() ) && this.extensionValidator.validate(albumDto.getCover()) ) {

                MultipartFileImage multipartFileImage = multipartFileImageFactory.create(
                        FunctionUtil.buildAlbumPath(principal.getUsername(), album.getId()));
                try {
                    album.setCover(multipartFileImage.saveImageAsSmall(albumDto.getCover()));
                }
                catch (IOException e) {
                    e.printStackTrace();
                }

                albumService.save(album);
            }
        }
        catch (HibernateException e) {
            e.printStackTrace();
            multipartFileImageFactory.create(album.getCover()).deleteDirectory();
            responseModel.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return responseModel;
    }

    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "edit album")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "formData [id, cover, title, description]", required = true),
            @ApiImplicitParam(name = "coverRemoveRequest - boolean value indicates to remove album cover")
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "album edited successfully"),
            @ApiResponse(code = 404, message = "album not found by formData.id")
    })
    @PutMapping("/edit")
    public ResponseModel albumEdit(@ApiIgnore @AuthenticationPrincipal User principal,
                                   @ApiParam("formData") @ModelAttribute(name = "album") AlbumDtoForm albumDto,
                                   @ApiParam("coverRemoveRequest") @RequestParam(required = false, defaultValue = "0") boolean coverRemoveRequest) {

        logger.info("edit albumd");
        logger.info(albumDto.toString());
        ResponseModel responseModel = new ResponseModel();
        String username = principal.getUsername();
        Album album = albumService.findByIdAndUserUsernameWithUser(albumDto.getId(), username);

        if ( album == null ) {
            responseModel.setStatus(HttpStatus.NOT_FOUND);
            return responseModel;
        }

        MultipartFileImage albumMultipartFileImageNew = multipartFileImageFactory.create(FunctionUtil.buildAlbumPath(username, album.getId()));

        if (coverRemoveRequest) {
            multipartFileImageFactory.create(album.getCover()).deleteImage();
            album.setCover(null);
        }
        else if ( albumDto.getCover() != null && ! albumDto.getCover().isEmpty()) {
            try {
                //delete old images
                multipartFileImageFactory.create(album.getCover()).deleteImage();
                //save new
                String coverPath = albumMultipartFileImageNew.saveImageAsSmall(albumDto.getCover());
                album.setCover(coverPath);
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
        album.setTitle(albumDto.getTitle());
        album.setDescription(albumDto.getDescription());

        if (albumService.save(album) == null) {
            // delete images for unsaved album
            albumMultipartFileImageNew.deleteImages();
        }

        return responseModel;
    }

    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "delete album")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "album deleted successfully"),
            @ApiResponse(code = 404, message = "album not found by id")
    })
    @DeleteMapping("/{id}/delete")
    public ResponseModel deleteAlbum(@ApiIgnore @AuthenticationPrincipal User principal,
                                     @ApiIgnore @PathVariable("id") Long albumId,
                                     @ApiIgnore HttpServletResponse response) {
        ResponseModel responseModel = new ResponseModel();
        Optional<Album> albumOptional = Optional.ofNullable(albumService.findByIdAndUserUsername(albumId, principal.getUsername()));
        albumOptional.ifPresentOrElse(
                (album) -> {
                    albumDelayedRemover.addEntity(album);
                },
                () -> {
                    responseModel.setStatus(HttpStatus.NOT_FOUND);
                }
        );
        return responseModel;
    }

    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "cancel album deletion (undo)")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "album deletion canceled")
    })
    @PostMapping("/delete/cancel/{id}")
    public void cancelDelete(@PathVariable Long id) {
        Optional<Album> optionalAlbum = albumService.findById(id);
        if (optionalAlbum.isPresent()) {
            // remove from list to be deleted
            albumDelayedRemover.removeEntity(optionalAlbum.get());
        }
    }

    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "get most new albums", responseContainer = "List")
    @GetMapping("/new")
    public ResponseModel getMostNew(){
        Collection<AlbumDto> albumDtos = albumService.findNew();
        /*System.out.println("New:");
        albumDtos.forEach(System.out::println);*/
        return new ResponseModel("albums", albumDtos);
    }

    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "get popular albums", responseContainer = "List")
    @GetMapping("/pop")
    public ResponseModel getMostPopular(){
        Collection<AlbumDto> albumDtos = albumService.findPopular();
        /*System.out.println("Popular:");
        albumDtos.forEach(System.out::println);*/
        return new ResponseModel("albums", albumDtos);
    }

    /**
     * Fetch all albums of current user subscriptions with images.<br/>
     * period values filter by: t (today) default; lw (last week); lm (last month)
     */
    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "get subscriptions albums")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "page"),
            @ApiImplicitParam(name = "offset"),
            @ApiImplicitParam(name = "limit"),
            @ApiImplicitParam(name = "f - filter by date")
    })
    @GetMapping("/sub-all")
    public ResponseModel albumsSubAll(
            @ApiIgnore @AuthenticationPrincipal User principal,
            @ApiParam("page") @RequestParam(value = "page", defaultValue = "0") Integer page,
            @ApiParam("offset") @RequestParam(value = "offset", defaultValue = "0") Integer offset,
            @ApiParam("limit") @RequestParam(value = "limit", required = false) Integer limit,
            @ApiParam("f") @RequestParam(value = "f", defaultValue = "lw") String dateFilter
    ) {
        Collection<User> subsFound = userService.findSubscriptionsOfUser(1L);
        UserData principalUserData = userDataService.findById(principal.getId()).get();
        try {
            principalUserData.setLastUpdateCheck(new Date());
            userDataService.save(principalUserData);
        }
        catch (NullPointerException e) {
            e.printStackTrace();
        }

        if (subsFound.size() == 0) {
            return new ResponseModel();
        }

        List<Long> ids = subsFound.stream().map(User::getId).collect(Collectors.toList());

//        long itemsTotal = albumService.countAllSubAlbums(ids);
        long itemsTotal = albumService.countNewUpdates(ids, principalUserData.getLastUpdateCheckSortStart());
        offset = FunctionUtil.resolveOffset(offset, page, pageItemsSize);
        limit = FunctionUtil.resolveLimit(limit, pageItemsSize);

        Map<String, List<AlbumDto>> usernameWithAlbums = albumService.findAllSubs(ids, offset, limit, principalUserData.getLastUpdateCheckSortStart(), dateFilter);
        ResponseModel responseModel = new ResponseModel(ResponseModel.ATTR_UPDATES, usernameWithAlbums);

        responseModel.addAttribute(ResponseModel.ATTR_PAGINATION,
                new PaginationData(offset, page, itemsTotal, usernameWithAlbums.values().size(), pageItemsSize));

        return responseModel;
    }

}
<file_sep>/src/main/java/com/gallery/model/dto/user/UserDto.java
package com.gallery.model.dto.user;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gallery.model.entity.album.Album;
import com.gallery.model.entity.user.User;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;

//@ApiModel(value = "DTO for User entity model")
@JsonInclude(value = JsonInclude.Include.NON_NULL)
@Getter
@Setter
//@ToString(exclude = {"albums"})
@NoArgsConstructor
public class UserDto {

    private Long id=0L;
    private String username="";
    private String email="";
    private String password="";
    @JsonProperty("password_confirm")
    private String passwordConfirm="";
    private Date createdAt;
    private boolean enabled;

    private Collection<Album> albums;
    private Set<User> subscribers;

    public UserDto(String username, String password, String email) {
        this.username = username;
        this.password = <PASSWORD>;
        this.email = email;
    }

    public UserDto(User user) {
        if (user == null) {
            return;
        }
        this.id = user.getId();
        this.username = user.getUsername();
        this.email = user.getEmail();
        this.createdAt = user.getCreatedAt();
        this.enabled = user.isEnabled();
    }

    public UserDto(User user, List<Album> albums) {
        this(user);
        this.albums = albums;
    }

    public UserDto(User user, Set<User> subscribers) {
        this(user);
        this.subscribers = subscribers;
    }

    public UserDto(User user, List<Album> albums, Set<User> subscribers) {
        this(user);
        this.albums = albums;
        this.subscribers = subscribers;
    }

    public User toUser() {
        return new User(this.username, this.password, this.email);
    }

    public UserDto toDto(User user) {
        return new UserDto(user);
    }

    @Override
    public String toString() {
        return "UserDto{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", password='" + <PASSWORD> + '\'' +
                ", passwordConfirm='" + <PASSWORD> + '\'' +
                ", createdAt=" + createdAt +
                '}';
    }
}
<file_sep>/build.gradle
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This is a general purpose Gradle build.
 * Learn how to create Gradle builds at https://guides.gradle.org/creating-new-gradle-builds
 */

plugins {
  id 'org.springframework.boot' version '2.3.3.RELEASE'
  id 'io.spring.dependency-management' version '1.0.10.RELEASE'
  id 'java'
}

repositories {
  mavenCentral()
}
dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
  implementation 'org.springframework.boot:spring-boot-starter-web'
  implementation 'org.springframework.boot:spring-boot-starter-security'
  //implementation 'org.springframework.boot:spring-boot-starter-mail:2.2.6'
  implementation 'org.springframework.boot:spring-boot-starter-mail:2.2.6.RELEASE'

  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'

  runtimeOnly 'com.h2database:h2'
  runtimeOnly 'mysql:mysql-connector-java'

  compileOnly 'org.springframework.boot:spring-boot-devtools'

  //compile 'com.auth0:java-jwt'
  //compile 'com.auth0:java-jwt:3.3.0'
  compile 'io.jsonwebtoken:jjwt:0.9.1'

  //implementation 'org.thymeleaf:thymeleaf:3.0.11.RELEASE'
  implementation 'org.thymeleaf:thymeleaf-spring5:3.0.11.RELEASE'

  //implementation 'org.springframework.boot:spring-boot-starter-amqp:2.3.12.RELEASE'
  implementation 'org.springframework.boot:spring-boot-starter-activemq:2.0.5.RELEASE'

//  implementation 'com.blazebit:blaze-persistence-core-api:1.6.0'
//  implementation 'com.blazebit:blaze-persistence-entity-view-api:1.6.0'
//  runtimeOnly 'com.blazebit:blaze-persistence-entity-view-impl:1.6.0'


//  compileOnly 'org.hibernate:hibernate-jpamodelgen:5.4.32.Final'
//  annotationProcessor('org.hibernate:hibernate-jpamodelgen:5.4.32.Final')

  testImplementation('org.springframework.boot:spring-boot-starter-test') {
   // exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
  }
  //testImplementation 'org.springframework.security:spring-security-test:5.3.9.RELEASE'
  //testImplementation 'org.testng:testng:7.4.0'

  implementation 'io.springfox:springfox-swagger2:2.9.2'
  implementation 'io.springfox:springfox-swagger-ui:2.9.2'

}

test {
  useJUnitPlatform()
}<file_sep>/src/test/resources/hibernate.properties
#IN MEMORY
#jdbc.driverClassName=org.h2.Driver
#jdbc.url=jdbc:h2:mem:gallery;DB_CLOSE_DELAY=-1
#hibernate.dialect=org.hibernate.dialect.H2Dialect
#hibernate.hbm2ddl.auto=create<file_sep>/src/test/java/com/gallery/service/UserServiceTest.java
package com.gallery.service;

import com.gallery.model.entity.user.User;
import com.gallery.service.common.ServiceTest;
import org.junit.Assert;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import java.util.Collection;
import java.util.Set;

public class UserServiceTest implements ServiceTest {

    private Logger logger = LoggerFactory.getLogger(UserServiceTest.class);

    @Autowired
    private UserService userService;

    @Test
    public void findAll() {
        Collection<User> users = (Collection<User>) userService.findAll();
        Assert.assertEquals(users.size(), 4);
    }

    @Test
    public void save() {
        long countBefore = count(userService);
        User user = new User("new User", "new-user-password", "<EMAIL>");
        User savedUser = userService.save(user);
        long countAfter = count(userService);
        Assert.assertNotNull(savedUser);
        Assert.assertEquals(countBefore + 1, countAfter);
    }

    @Test
    public void delete() {
        long countBefore = count(userService);
        User user1 = userService.findByUsername("Solomon");
        Assert.assertNotNull(user1);
        userService.delete(user1);
        long countAfter = count(userService);
        Assert.assertEquals(countBefore - 1, countAfter);
    }

    @Test
    @Order(Order.DEFAULT + 1)
    public void findByUsername() {
        String username = "Solomon";
        User user = userService.findByUsername(username);
        Assert.assertNotNull(user);
        Assert.assertEquals(username, user.getUsername());
    }

    @Test
    @Order(Order.DEFAULT + 2)
//    @Commit
    @Rollback(false)
    public void subscribeUser() {
        //  Elaine subscribes to Dominick
        User elaine_Sexton = userService.findByUsername("Elaine");
        User dominick_Garza = userService.findByUsername("Dominick");

        Assert.assertNotNull(elaine_Sexton);
        Assert.assertNotNull(dominick_Garza);

        int elaine_SextonSubscriptionsBefore = userService.findSubscriptionsOfUser(elaine_Sexton.getId()).size();

        Assert.assertFalse(
                dominick_Garza.getSubscribers().contains(elaine_Sexton)
        );

        dominick_Garza.getSubscribers().add(elaine_Sexton);
        userService.save(dominick_Garza);

        Set<User> dominick_GarzaSubscribers = userService.findSubscribersByUserId(dominick_Garza.getId());
        Assert.assertTrue(
                dominick_GarzaSubscribers.contains(elaine_Sexton)
        );

        int elaine_SextonSubscriptionsAfter = userService.findSubscriptionsOfUser(elaine_Sexton.getId()).size();
        Assert.assertEquals(elaine_SextonSubscriptionsBefore + 1, elaine_SextonSubscriptionsAfter);
    }

    @Test
    @Order(Order.DEFAULT + 3)
//    @Commit
    @Rollback(false)
    public void unsubscribeUser() {
        // Elaine unsubscribes from <NAME>
        User elaine_Sexton = userService.findByUsername("elaine");
        User dominick_Garza = userService.findByUsername("dominick");

        Assert.assertNotNull(elaine_Sexton);
        Assert.assertNotNull(dominick_Garza);

        Set<User> dominick_GarzaSubs = userService.findSubscribersByUserId(dominick_Garza.getId());
        Assert.assertTrue(
                dominick_GarzaSubs.contains(elaine_Sexton)
        );

        dominick_GarzaSubs.remove(elaine_Sexton);

        dominick_Garza.setSubscribers(dominick_GarzaSubs);
        userService.save(dominick_Garza);

        dominick_GarzaSubs = userService.findSubscribersByUserId(dominick_Garza.getId());
        Assert.assertFalse(
                dominick_GarzaSubs.contains(elaine_Sexton)
        );
    }

}
<file_sep>/src/main/java/com/gallery/config/ResponseModelErrorHandler.java
package com.gallery.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gallery.util.ResponseModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

//@RestControllerAdvice
public class ResponseModelErrorHandler {

    private int errorStatus = HttpStatus.INTERNAL_SERVER_ERROR.value();

    @Autowired
    private ObjectMapper objectMapper;

    @ExceptionHandler(value = Exception.class)
    public void handle(HttpServletRequest request, HttpServletResponse response) {
        if(response.getStatus() == errorStatus) {
            try ( PrintWriter printWriter = response.getWriter() ) {
                printWriter.write(objectMapper.writeValueAsString(new ResponseModel(HttpStatus.INTERNAL_SERVER_ERROR)));
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
<file_sep>/src/main/java/com/gallery/model/entity/user/Role.java
package com.gallery.model.entity.user;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import javax.persistence.*;

//@ApiModel
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
@Entity(name = "Role")
@Table(name = "role")
public class Role {
    public static String ROLE_USER = "USER";
    public static String ROLE_ADMIN = "ADMIN";

    public static String ROLE_USER_VALUE = "ROLE_USER";
    public static String ROLE_ADMIN_VALUE = "ROLE_ADMIN";

//    @ApiModelProperty(name = "id", dataType = "Long", required = true)
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

//    @ApiModelProperty(name = "name", dataType = "String", required = true, position = 1)
    @Column(name = "name", nullable = false)
    private String name;

    public Role(String name) {
        this.name = name;
    }
}
<file_sep>/src/main/java/com/gallery/service/entity/SingleEntityService.java
package com.gallery.service.entity;

public interface SingleEntityService extends Fetchable{

    <T> T findOneByAttrWithOptions(Class<T> entityClass, String attrName, String attrVal, String... fetchOptions);

    /*default <T> void applyOptionsForCriteriaBuilder(CriteriaBuilder cb, CriteriaQuery<T> criteriaQuery, Root<T> root, EntityServiceOptions options) {
        if (options.hasOrderAsc()) {
            criteriaQuery.orderBy(cb.asc(root.get(options.getOrderAscValue())));
        } else if (options.hasOrderDesc()) {
            criteriaQuery.orderBy(cb.desc(root.get(options.getOrderDescValue())));
        }
    }

    default <T> void applyOptionsForTypedQuery(TypedQuery<T> typedQuery, EntityServiceOptions options) {
        if (options.hasOffset()) {
            try {
                typedQuery.setFirstResult(options.getOffsetValue());
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
        }
        if (options.hasLimit()) {
            try {
                typedQuery.setMaxResults(options.getLimitValue());
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
        }
    }*/
}
<file_sep>/src/main/java/com/gallery/service/delayedtask/AlbumDelayedRemover.java
package com.gallery.service.delayedtask;

import com.gallery.model.entity.album.Album;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Service;
import java.util.LinkedList;
import java.util.Queue;

@Service("albumRemover")//albumDelayedRemover
public class AlbumDelayedRemover extends AbstractDelayedEntityRemover<Album> {

    private static Logger logger = LoggerFactory.getLogger(AlbumDelayedRemover.class);

    private volatile Queue<TaskItem<Album>> items = new LinkedList<>();

    @Value("${undo.album.lifetime.millis}")
    protected Long lifeTime;

    @Autowired
    public AlbumDelayedRemover(@Qualifier("albumRepository") CrudRepository<Album, Long> repository) {
        super(repository);
    }

    @Override
    public void addEntity(Album entity) {
        entity.setDeleted(true);
        repository.save(entity);
        TaskItem<Album> taskItem = new TaskItem<>(lifeTime, entity);
        getItems().add(taskItem);
        System.out.println("entity added: " + entity.toString());
        if ( ! running) {
            running = true;
            run();
        }
    }

    @Override
    public Queue<TaskItem<Album>> getItems() {
        return items;
    }

    @Override
    public boolean removeEntity(Album entity) {
        boolean isRemoved = getItems().removeIf(taskItem -> taskItem.getKeyId().equals(entity.getId()));
        if(isRemoved){
            entity.setDeleted(false);
            repository.save(entity);
        }
        return isRemoved;
    }

}
<file_sep>/src/main/java/com/gallery/App.java
package com.gallery;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ResourceBundleMessageSource;

//@SpringBootApplication//(exclude = {DataSourceAutoConfiguration.class})
//@EnableJpaRepositories(basePackages = {"emrissol.repository"})
//@EnableAutoConfiguration
@SpringBootApplication(exclude = ActiveMQAutoConfiguration.class)
//@EnableJms
public class App {

//  @Qualifier("entityManager")
//  @PersistenceContext
//  EntityManager entityManager;

//  @PersistenceContext
//  private EntityManager entityManager;


//  @Autowired
//  MailSenderUtil mailSenderUtil;

  public static void main(String[] args) {
    SpringApplication.run(App.class, args);
  }


  @Bean("validationMessageSource")
  public MessageSource resourceBundleMessageSourceBean() {
      ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
      messageSource.setBasename("validation/validation");
      messageSource.setUseCodeAsDefaultMessage(true);
      return messageSource;
  }
}<file_sep>/src/main/java/com/gallery/jms/MessageingService.java
package com.gallery.jms;

public interface MessageingService<T> {
    void send(T object);
}
<file_sep>/src/main/java/com/gallery/model/dto/album/AlbumDtoForm.java
package com.gallery.model.dto.album;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.web.multipart.MultipartFile;

@Getter
@Setter
@ToString
@NoArgsConstructor
public class AlbumDtoForm {
    private Long id;
    private String title;
    private String description;
    private MultipartFile cover;
}
<file_sep>/src/main/java/com/gallery/util/MailSenderService.java
package com.gallery.util;

import lombok.AccessLevel;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.File;

@Component(value = "commonMailSenderService")
public class MailSenderService {

    @Getter(AccessLevel.PROTECTED)
    private JavaMailSender mailSender;

    private SpringTemplateEngine templateEngine;

    @Value("${client.host.url}")
    private String clientHostUrl;

    @Autowired
    public MailSenderService(JavaMailSender mailSender, SpringTemplateEngine springTemplateEngine) {
        this.mailSender = mailSender;
        this.templateEngine = springTemplateEngine;
    }

    public void sendMimeEmail(String text) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setTo(new InternetAddress("<EMAIL>"));
            helper.setText("mime message");
            FileSystemResource fileSystemResource = new FileSystemResource(new File(""));
            helper.addAttachment("id12", fileSystemResource);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }


    public void sendTextEmail(String text) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        simpleMailMessage.setTo("<EMAIL>");
        simpleMailMessage.setSubject("My Spring App");
        simpleMailMessage.setText(text);

        try {
            mailSender.send(simpleMailMessage);
        } catch (MailException e) {
            e.printStackTrace();
        }
    }


    public void sendChangeRequestEmail(String emailTo, String uri, String token, String changeSubject) {
        String tokenLink = clientHostUrl + uri + token;
        String tokenHtmlLink = new String("<a href='" + tokenLink + "'>link</a>");
        String body = new String("You have been requested for change your " + changeSubject.toLowerCase() + ". Click the " + tokenHtmlLink + " to proceed.<br>Ignore this email if you did not send such a request!");
        String footer = new String("<empty footer>");
        sendEmail("email",
                emailTo,
                changeSubject + " change request",
                changeSubject + " change request",
                body,
                footer);
    }

    public void sendPasswordChangeEmail(String emailTo, String token) {
        sendChangeRequestEmail(emailTo, "/change-password-request/", token, "Password");
    }

    public void sendEmailChangeEmail(String emailTo, String token) {
        sendChangeRequestEmail(emailTo, "/change-email-request/", token, "Email");
    }

    public void sendConfirmationEmail(String emailTo, String authToken) {
        String header = new String(AppConfig.APP_NAME + " | Registration confirmation");
        String tokenLink = clientHostUrl + "/register/" + authToken;
        String tokenHtmlLink = new String("<a href='" + tokenLink + "'>link</a>");
        String body = new String("Please follow this " + tokenHtmlLink + " to confirm your registration.");
        sendEmail("email", emailTo, header, header, body, "");
    }

    public void sendEmail(String template, String emailTo, String subject, String header, String body, String footer) {
        final MimeMessage mimeMessage = mailSender.createMimeMessage();
        try {
            final MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
            mimeMessageHelper.setTo(emailTo);
            mimeMessageHelper.setSubject(subject);

            Context context = new Context();
            context.setVariable("header", header);
            context.setVariable("body", body);
            context.setVariable("footer", footer);
            final String emailContent = templateEngine.process(template, context);
            mimeMessageHelper.setText(emailContent, true);
            mailSender.send(mimeMessageHelper.getMimeMessage());
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

//    public void sendHtmlEmail(){
//        final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
//        Optional<MimeMessageHelper> optionalMimeMessageHelper = Optional.empty();
//        Optional<String> optionalS = Optional.empty();
//        try {
//            final MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
//            mimeMessageHelper.setTo("<EMAIL>");
//            mimeMessageHelper.setSubject("My Spring App");
//
//            Context context = new Context();
//            String logoUrl = "media/album/regular/42/1607522691349DsL9sK.jpg";
//            context.setVariable("logo", logoUrl);
////            context.setVariable("logo", "logo.jpg");
//            context.setVariable("header", "HEADER CONTENT");
//            context.setVariable("body", "BODY CONTENT");
//            context.setVariable("footer", "FOOTER CONTENT");
//            final String emailContent = this.templateEngine.process("email", context);
//            System.out.println(emailContent);
////            mimeMessageHelper.setText(emailContent, true);
////            mimeMessageHelper.addInline("logo.jpg", new FileSystemResource("src/main/webapp/media/album/regular/42/1607522691349DsL9sK.jpg"));
////            optionalMimeMessageHelper = Optional.of(mimeMessageHelper);
//        } catch (MessagingException e) {
//            e.printStackTrace();
//        }
//        optionalMimeMessageHelper.ifPresentOrElse(
//                (helper) -> {
//                    System.out.println("sending html email");
//                    this.mailSender.send(helper.getMimeMessage());
//                },
//                () -> System.out.println("MimeMessageHelper is null")
//        );
//    }

//    @Bean
//    public ResourceBundleMessageSource emailMessageSource(){
//        final ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
//        resourceBundleMessageSource.setBasename("mail/MailMessages");
//        return resourceBundleMessageSource;
//    }

}
<file_sep>/src/test/java/com/gallery/web/client/UnknownControllerTest.java
package com.gallery.web.client;

import com.gallery.FunctionUtilTest;
import com.gallery.model.entity.user.User;
import com.gallery.service.UserService;
import com.gallery.util.ResponseModel;
import com.gallery.web.client.common.ControllerClientTest;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.ExpectedCount;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import java.util.List;

//@ContextConfiguration(classes = {App.class})
//@WebAppConfiguration
//public class UserControllerTest extends AbstractTestNGSpringContextTests implements AppTest {
public class UnknownControllerTest extends ControllerClientTest {

    @Value("${controller.uri.user.all}")
    protected String userFindAllUri;

//    private MockMvc mockMvc;
//    @Autowired
//    private WebApplicationContext webContext;
//    private WebApplicationContext webContext;
//    @Autowired private MockRestServiceServer server;

//    @BeforeAll
//    void setup(){
//        mockMvc = MockMvcBuilders.webAppContextSetup(webContext).build();
//    }

    /*@BeforeAll
    private void setup() {
        mockMvc = MockMvcBuilders
                .webAppContextSetup(webContext)
                .apply(SecurityMockMvcConfigurers.springSecurity()) // enable security for the mock set up
                .build();
    }*/
    /*@Test
    public void secur(WebApplicationContext webContext) throws Exception {
        *//*mockMvc = MockMvcBuilders
                .standaloneSetup(webContext)
//                .apply(SecurityMockMvcConfigurers.springSecurity()) // enable security for the mock set up
                .build();*//*

        MockHttpServletResponse response = mockMvc.perform(
                MockMvcRequestBuilders.get("/api/user/secur"))
                .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andReturn().getResponse();

        System.out.println(response.getContentAsString());
    }*/

    @Autowired
    private UserService userService;

    @Test
    @Timeout(5)
    public void findAllUsers() {
        List<User> usersFound = (List<User>) userService.findAll();
        ResponseModel responseModelExpected = new ResponseModel("users", usersFound);
        String expectedJsonContent = asJson(responseModelExpected);

        RestTemplate restTemplate = new RestTemplate();
        server = MockRestServiceServer.bindTo(restTemplate).build();
        server.expect(ExpectedCount.once(), requestTo(userFindAllUri))
                .andExpect(method(HttpMethod.GET))
//                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
//                .andRespond(withSuccess(expectedJson, MediaType.APPLICATION_JSON))
                .andRespond(
                        withStatus(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(expectedJsonContent));

        ResponseModel responseModelResponse = restTemplate.getForObject(userFindAllUri, ResponseModel.class);
        server.verify();
        int usersSize = FunctionUtilTest.getResponseModelCollectionAttrSize(responseModelResponse, ResponseModel.ATTR_USERS);
        Assert.assertEquals(HttpStatus.OK.value(), responseModelResponse.getStatusCode());
        Assert.assertEquals(4, usersSize);

    }

}
<file_sep>/src/main/java/com/gallery/service/entity/MultiEntityServiceImpl.java
package com.gallery.service.entity;

import org.hibernate.loader.MultipleBagFetchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Collection;

//@Service
public class MultiEntityServiceImpl implements MultiEntityService {

    protected ApplicationContext applicationContext;
    protected EntityManager entityManager;

    @Autowired
    public MultiEntityServiceImpl(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.entityManager = this.applicationContext.getBean(EntityManager.class);
    }

    /**
     * Find all entities with multiple join fetch options.
     *
     * @param entityClass  entity class (e.g. User.class)
//     * @param options      query options
     * @param fetchOptions entity associations
     * @return List&lt;T&gt; of entitites or empty
     */
    @Override
    public <T> Collection<T> findAllByClassWithOptions(Class<T> entityClass, String... fetchOptions) {

        CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
        CriteriaQuery<T> cq = cb.createQuery(entityClass);
        cq.distinct(true);
        Root<T> root = cq.from(entityClass);
        fetch(root, fetchOptions);
        TypedQuery<T> tq = this.entityManager.createQuery(cq.select(root));
        Collection<T> resultList = new ArrayList<>();
        try {
            resultList = tq.getResultList();
        } catch (NoResultException | MultipleBagFetchException ignored) {
        }

        return resultList;
    }


}
<file_sep>/src/main/java/com/gallery/model/entity/album/Image.java
package com.gallery.model.entity.album;

import com.gallery.model.entity.AbstractEntity;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

//@ApiModel(value = "Image entity model", parent = AbstractEntity.class)
@Getter
@Setter
@NoArgsConstructor
@ToString(callSuper = true)
@Entity(name = "Image")
@Table(name = "image")
public class Image extends AbstractEntity {

//    @ApiModelProperty(name = "resource file path", dataType = "String", required = true, position = 2)
    @Column(name = "src")
    private String src;

//    @ApiModelProperty(name = "image view count", dataType = "String", allowEmptyValue = true, position = 3)
    @Column(name = "view_count", nullable = false)
    private Integer viewCount = 0;

   /* @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    private Album album;
*/
    public Image(String src) {
        this.src = src;
    }

}
<file_sep>/src/main/resources/validation/validation.properties
field.string.length.max=24
field.string.length.min=3
field.password.length.min=6
field.password.length.max=28

field.required={0} field is required
field.short={0} field is too short
field.long={0} field is too long
field.email.format=wrong email format

user.field.duplicate=user with such {0} already exists
field.password.mismatch=passwords do not match

field.required={0} field is required
field.short={0} field is too short
field.long={0} field is too long
field.email.format=wrong email format

user.field.duplicate=user with such {0} already exists
field.password.mismatch=passwords do not match

extension.format=wrong extension format<file_sep>/src/main/java/com/gallery/validation/UserValidator.java
package com.gallery.validation;

import com.gallery.model.dto.user.UserDto;
import com.gallery.service.UserService;
import com.gallery.validation.field.EmailFieldValidator;
import com.gallery.validation.field.PasswordFieldValidator;
import com.gallery.validation.field.UsernameFieldValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

//spring classpath: /src/main/resources
//@PropertySource("classpath:validation.properties")
@Component
public class UserValidator implements Validator {

    private MessageSource messageSource;
    private UserService userService;
    private UsernameFieldValidator usernameFieldValidator;
    private PasswordFieldValidator passwordFieldValidator;
    private EmailFieldValidator emailFieldValidator;

    public UserValidator(
            @Qualifier("validationMessageSource") MessageSource messageSource,
            UserService userService,
            UsernameFieldValidator usernameFieldValidator,
            PasswordFieldValidator passwordFieldValidator,
            EmailFieldValidator emailFieldValidator) {
        this.messageSource = messageSource;
        this.userService = userService;
        usernameFieldValidator.setFieldName("username");
        this.usernameFieldValidator = usernameFieldValidator;
        passwordFieldValidator.setFieldName("password");
        this.passwordFieldValidator = passwordFieldValidator;
        emailFieldValidator.setFieldName("email");
        this.emailFieldValidator = emailFieldValidator;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return UserDto.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        UserDto user = (UserDto) o;
        this.usernameFieldValidator.validate(user.getUsername(), errors);
        this.emailFieldValidator.validate(user.getEmail(), errors);
        this.passwordFieldValidator.validate(user.getPassword(), errors);

        if (!user.getPasswordConfirm().equals(user.getPassword())) {
            errors.rejectValue("passwordConfirm", this.usernameFieldValidator.getMessageSource().getMessage("field.password.mismatch", null, null));
        }

        if( ! errors.hasErrors()){
            if (userService.findByUsername(user.getUsername()) != null) {
                errors.rejectValue("username", this.messageSource.getMessage("user.field.duplicate", new String[]{"username"}, null));
            }
            else if (userService.findByEmail(user.getEmail()) != null) {
                errors.rejectValue("email", this.messageSource.getMessage("user.field.duplicate", new String[]{"email"}, null));
            }
        }
    }


}<file_sep>/src/main/java/com/gallery/util/factory/Factory.java
package com.gallery.util.factory;

public interface Factory<T> {
    T create(String endPath);
}
<file_sep>/src/main/java/com/gallery/service/entity/option/EntityServiceOptions.java
package com.gallery.service.entity.option;

import lombok.Getter;
import java.util.HashMap;
import java.util.Map;

public class EntityServiceOptions {

    public static String KEY_OFFSET = "offset";
    public static String KEY_LIMIT = "limit";
    public static String KEY_ORDER_ASC = "orderAsc";
    public static String KEY_ORDER_DESC = "orderDesc";


    @Getter
    protected Map<String, String> options = new HashMap<>();


    public EntityServiceOptions() {
    }

    protected EntityServiceOptions(Map<String, String> options) {
        this.options = options;
    }


    public boolean hasValueForKey(String key) {
        return options.containsKey(key);
    }

    public String getValue(String key) {
        return options.get(key);
    }

    public boolean hasLimit() {
        return hasValueForKey(KEY_LIMIT);
    }

    public int getLimitValue() throws NumberFormatException {
        return Integer.parseInt(getValue(KEY_LIMIT));
    }

    public boolean hasOffset() {
        return hasValueForKey(KEY_OFFSET);
    }

    public int getOffsetValue() throws NumberFormatException {
        return Integer.parseInt(getValue(KEY_OFFSET));
    }

    public boolean hasOrderAsc() {
        return hasValueForKey(KEY_ORDER_ASC);
    }

    public String getOrderAscValue() {
        return getValue(KEY_ORDER_ASC);
    }

    public boolean hasOrderDesc() {
        return hasValueForKey(KEY_ORDER_DESC);
    }

    public String getOrderDescValue() {
        return getValue(KEY_ORDER_DESC);
    }

    public static Builder builder() {
        return new Builder();
    }


    public static class Builder {

        private Map<String, String> options = new HashMap<>();

        public Builder withLimit(int limit) {
            options.put(KEY_LIMIT, String.valueOf(limit));
            return this;
        }

        public Builder withOffset(int offset) {
            options.put(KEY_OFFSET, String.valueOf(offset));
            return this;
        }

        public Builder withOrderAsc(String fieldName) {
            options.put(KEY_ORDER_ASC, fieldName);
            return this;
        }

        public Builder withOrderDesc(String fieldName) {
            options.put(KEY_ORDER_DESC, fieldName);
            return this;
        }

        public EntityServiceOptions build() {
            return new EntityServiceOptions(options);
        }

    }

}
<file_sep>/src/main/java/com/gallery/web/UserController.java
package com.gallery.web;

import com.gallery.model.dto.user.UserDto;
import com.gallery.model.entity.user.User;
import com.gallery.model.entity.user.UserData;
import com.gallery.service.UserDataService;
import com.gallery.service.UserService;
import com.gallery.util.ResponseModel;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    private UserService userService;
    private UserDataService userDataService;

    @Autowired
    public UserController(UserService userService, UserDataService userDataService) {
        this.userService = userService;
        this.userDataService = userDataService;
    }

//    @Autowired
//    EntityService entityService;

    @PostMapping("/get")
    public ResponseModel getUser(@AuthenticationPrincipal User user,
                                 @RequestParam(name = "options", required = false) List<String> options) {
//        if(user != null && options.size() > 0){
//            User user1 = entityService.findOneByAttrWithOptions( User.class, User_.id, user.getId().toString(), (String[]) options.toArray() );
//        }
        UserDto userDto = null;
        if (user != null) {
            userDto = new UserDto(user);
        }
        ResponseModel responseModel = new ResponseModel("user", userDto);
        return responseModel;
    }

    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "save current logged in user's theme pref setting")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "theme preference setting saved")
    })
    @PostMapping("/theme")
    public void setTheme(@ApiIgnore @AuthenticationPrincipal User principal, @ApiIgnore HttpServletRequest request) {
        UserData principalUserData = userDataService.findById(principal.getId()).get();
        String theme = request.getParameter("theme");
        try {
            principalUserData.setTheme(Integer.parseInt(theme));
        }
        catch (NullPointerException | NumberFormatException e) {
            e.printStackTrace();
        }

    }

}
<file_sep>/src/main/java/com/gallery/util/FunctionUtil.java
package com.gallery.util;

import com.gallery.util.image.ImageSaver;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import net.bytebuddy.utility.RandomString;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import javax.persistence.Query;
import java.util.*;
import java.util.regex.Pattern;

public class FunctionUtil {

    public static final Pattern CLEAN_STRING_PATTERN = Pattern.compile("[{}!<>.,;%&@#/\\]\\[\\\\$^*()?|_\\s]*");
//    public static final Pattern BACKSLASHES_PATTERN = Pattern.compile("\\\\");


    public static String buildAlbumPath(String albumUserUsername, Long id) {
        return ImageSaver.ROOT_CATALOG + "/album/" + cleanString(albumUserUsername.toLowerCase()) + "/" + id;
    }

    public static String buildImagePath(String albumUserUsername, Long id) {
        return buildAlbumPath(albumUserUsername.toLowerCase(), id) + "/images";
    }

    public static String cleanString(String string) {
        return CLEAN_STRING_PATTERN.matcher(string).replaceAll("");
    }

    public static String getFileExtension(String fileName, boolean dot) {
//        int lastIndexOf = fileName.lastIndexOf(".");
//        if (lastIndexOf > 0) {
//            return fileName.substring(dot ? lastIndexOf : (lastIndexOf + 1));
//        }
//        return "";
        String ext = StringUtils.getFilenameExtension(fileName);
        return dot ? ("." + ext) : ext;
    }

    public static Map<String, String> mapBindingResultFieldErrors(BindingResult errors) {
        Map<String, String> errorsMap = new HashMap<>();
        if (errors.hasErrors()) {
            for (FieldError fieldError : errors.getFieldErrors()) {
                if ( ! errorsMap.containsKey(fieldError.getField())) {//put only 1st error for each field
                    errorsMap.put(fieldError.getField(), fieldError.getCode());
                }
            }
        }
        return errorsMap;
    }

    public static Integer resolveOffset(Integer offset, Integer page, int itemsSizePerPage){
        if(page != null && page > 0){
            offset = page * itemsSizePerPage;
        }
        return offset;
    }
    public static Integer resolveLimit(Integer limit, int itemsSizePerPage){
        if(limit == null || limit == 0){
            limit = itemsSizePerPage;
        }
        return limit;
    }

    public static void applyOffsetAndLimit(Query typedQuery, Integer offset, Integer limit){
        if (offset != null && offset > 0) {
            typedQuery.setFirstResult(offset);
        }
        if (limit != null && limit > 1) {
            typedQuery.setMaxResults(limit);
        }
    }


    public static AbstractMap.SimpleEntry< String, AbstractMap.SimpleEntry<String, String> > getWhereClauseEntry(
            String rootKey,
            String attrKey, String attrVal) {

        return new AbstractMap.SimpleEntry<>(
                rootKey,
                new AbstractMap.SimpleEntry(attrKey, attrVal)
        );
    }
//    public static String removeBackslashes(String string) {
//        return BACKSLASHES_PATTERN.matcher(string).replaceAll("\\/");
//    }
}
<file_sep>/gallery.sql
-- phpMyAdmin SQL Dump
-- version 4.9.0.1
-- https://www.phpmyadmin.net/
--
-- Host: localhost
-- Generation Time: Jul 22, 2021 at 05:25 PM
-- Server version: 8.0.19
-- PHP Version: 7.1.19

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Database: `gallery`
--

-- --------------------------------------------------------

--
-- Table structure for table `album`
--

CREATE TABLE `album` (
  `id` bigint NOT NULL,
  `created_at` date DEFAULT NULL,
  `cover` varchar(255) DEFAULT NULL,
  `deleted` tinyint(1) DEFAULT '0',
  `description` text,
  `title` varchar(255) NOT NULL,
  `user_id` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `album`
--

INSERT INTO `album` (`id`, `created_at`, `cover`, `deleted`, `description`, `title`, `user_id`) VALUES
(1, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #1', 1),
(2, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #2', 1),
(3, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #3', 1),
(4, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #4', 1),
(5, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #5', 1),
(6, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #6', 1),
(7, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #7', 1),
(8, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #8', 1),
(9, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #9', 1),
(10, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #10', 1),
(11, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #11', 1),
(12, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #12', 1),
(13, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #13', 1),
(14, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #14', 1),
(15, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #15', 1),
(16, '2021-06-16', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #16', 1),
(17, '2021-07-13', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #39', 2),
(18, '2021-07-15', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #40', 2),
(19, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #41', 2),
(20, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #42', 2),
(21, '2021-07-06', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #43', 2),
(22, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #44', 2),
(23, '2021-07-07', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #45', 2),
(24, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #46', 2),
(25, '2021-06-01', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #47', 2),
(26, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #48', 2),
(27, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #49', 2),
(28, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #50', 2),
(29, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #51', 2),
(30, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #52', 2),
(31, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #53', 2),
(32, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #54', 2),
(33, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #55', 2),
(34, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #56', 2),
(35, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #17', 4),
(36, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #18', 4),
(37, '2021-07-18', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #19', 4),
(38, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #20', 4),
(39, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #21', 4),
(40, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #22', 4),
(41, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #23', 4),
(42, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #24', 4),
(43, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #25', 4),
(44, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #26', 4),
(45, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #27', 4),
(46, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #28', 4),
(47, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #29', 4),
(48, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #30', 4),
(49, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #31', 4),
(50, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #32', 4),
(51, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #33', 4),
(52, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #34', 4),
(53, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, NULL, 'album #35', 4),
(54, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #36', 4),
(55, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, NULL, 'album #37', 4),
(56, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, NULL, 'album #38', 4);

-- --------------------------------------------------------

--
-- Table structure for table `image`
--

CREATE TABLE `image` (
  `id` bigint NOT NULL,
  `created_at` date DEFAULT NULL,
  `src` varchar(255) DEFAULT NULL,
  `view_count` int NOT NULL,
  `album_id` bigint NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `image`
--

INSERT INTO `image` (`id`, `created_at`, `src`, `view_count`, `album_id`) VALUES
(1, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 1),
(2, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 1),
(3, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 1),
(4, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 1),
(5, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 1),
(6, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 1),
(7, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 1),
(8, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 1),
(9, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 1),
(10, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 2),
(11, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 2),
(12, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 2),
(13, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 2),
(14, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 2),
(15, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 2),
(16, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 2),
(17, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 2),
(18, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 3),
(19, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 3),
(20, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 3),
(21, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 3),
(22, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 3),
(23, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 3),
(24, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 3),
(25, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 4),
(26, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 4),
(27, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(28, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 4),
(29, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 4),
(30, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 4),
(31, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(32, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 4),
(33, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 4),
(34, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 4),
(35, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(36, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(37, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(38, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(39, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 4),
(40, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(41, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 4),
(42, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 4),
(43, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 5),
(44, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(45, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 5),
(46, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 5),
(47, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 5),
(48, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(49, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(50, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(51, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 5),
(52, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 5),
(53, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(54, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(55, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(56, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 5),
(57, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 5),
(58, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 5),
(59, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 5),
(60, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 6),
(61, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 6),
(62, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 6),
(63, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 6),
(64, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 6),
(65, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 6),
(66, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 6),
(67, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 6),
(68, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 6),
(69, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 6),
(70, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 6),
(71, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 7),
(72, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 7),
(73, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 7),
(74, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 8),
(75, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 8),
(76, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 8),
(77, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 9),
(78, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 9),
(79, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 9),
(80, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 9),
(81, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 9),
(82, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 9),
(83, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 9),
(84, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 9),
(85, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 9),
(86, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 9),
(87, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 9),
(88, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 9),
(89, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 9),
(90, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 10),
(91, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 10),
(92, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 10),
(93, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 10),
(94, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 10),
(95, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 10),
(96, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 10),
(97, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 10),
(98, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 10),
(99, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 10),
(100, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 10),
(101, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 10),
(102, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 10),
(103, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 10),
(104, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 10),
(105, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 10),
(106, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 11),
(107, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 11),
(108, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 11),
(109, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 11),
(110, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 11),
(111, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(112, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(113, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(114, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(115, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 12),
(116, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(117, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(118, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 12),
(119, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(120, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 12),
(121, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 13),
(122, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 13),
(123, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 13),
(124, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 13),
(125, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 13),
(126, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 13),
(127, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 13),
(128, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 13),
(129, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 13),
(130, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 14),
(131, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(132, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 14),
(133, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(134, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 14),
(135, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 14),
(136, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 14),
(137, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 14),
(138, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(139, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(140, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 14),
(141, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(142, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(143, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 14),
(144, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(145, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 14),
(146, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 14),
(147, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 14),
(148, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 14),
(149, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 15),
(150, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 15),
(151, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 15),
(152, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 15),
(153, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 15),
(154, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 16),
(155, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(156, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(157, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 16),
(158, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 16),
(159, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(160, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 16),
(161, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 16),
(162, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 16),
(163, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 16),
(164, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 16),
(165, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(166, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(167, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 16),
(168, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(169, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(170, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(171, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 16),
(172, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 16),
(173, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 16),
(174, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 17),
(175, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 17),
(176, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 17),
(177, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 17),
(178, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 17),
(179, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 17),
(180, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 17),
(181, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 17),
(182, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 17),
(183, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 17),
(184, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 17),
(185, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 17),
(186, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 18),
(187, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 18),
(188, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 18),
(189, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 18),
(190, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 18),
(191, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 18),
(192, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 19),
(193, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 19),
(194, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 19),
(195, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 19),
(196, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 19),
(197, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 19),
(198, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 19),
(199, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 19),
(200, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(201, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(202, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 20),
(203, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(204, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 20),
(205, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 20),
(206, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(207, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 20),
(208, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 20),
(209, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 20),
(210, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 20),
(211, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 20),
(212, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(213, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(214, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 20),
(215, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 20),
(216, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 20),
(217, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(218, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(219, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 20),
(220, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 20),
(221, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 21),
(222, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 21),
(223, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 21),
(224, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 21),
(225, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 21),
(226, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 21),
(227, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 21),
(228, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 21),
(229, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 21),
(230, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 21),
(231, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 21),
(232, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 21),
(233, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 21),
(234, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 21),
(235, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 21),
(236, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 21),
(237, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 22),
(238, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 22),
(239, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 22),
(240, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 22),
(241, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 22),
(242, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 22),
(243, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 22),
(244, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 22),
(245, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 22),
(246, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 22),
(247, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 22),
(248, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 22),
(249, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 22),
(250, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 22),
(251, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 22),
(252, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 23),
(253, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 23),
(254, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 23),
(255, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 23),
(256, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 23),
(257, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 23),
(258, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 23),
(259, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 23),
(260, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 23),
(261, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 23),
(262, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 23),
(263, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 23),
(264, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 23),
(265, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 23),
(266, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 24),
(267, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 25),
(268, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 25),
(269, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 25),
(270, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 25),
(271, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 25),
(272, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 25),
(273, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 25),
(274, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 25),
(275, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 26),
(276, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 26),
(277, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 26),
(278, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 26),
(279, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 26),
(280, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 26),
(281, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 27),
(282, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 27),
(283, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(284, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 28),
(285, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(286, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 28),
(287, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(288, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(289, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 28),
(290, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 28),
(291, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 28),
(292, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 28),
(293, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(294, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(295, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 28),
(296, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(297, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 28),
(298, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 28),
(299, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 28),
(300, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 28),
(301, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 28),
(302, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 28),
(303, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 29),
(304, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 29),
(305, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 30),
(306, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 30),
(307, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 30),
(308, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(309, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 31),
(310, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 31),
(311, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(312, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(313, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 31),
(314, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(315, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 31),
(316, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(317, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 31),
(318, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 31),
(319, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 31),
(320, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(321, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(322, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(323, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 31),
(324, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(325, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 31),
(326, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 31),
(327, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 31),
(328, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 32),
(329, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 32),
(330, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(331, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 32),
(332, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 32),
(333, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(334, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(335, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 32),
(336, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 32),
(337, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 32),
(338, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(339, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(340, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(341, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 32),
(342, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 32),
(343, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 32),
(344, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 32),
(345, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 32),
(346, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(347, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 32),
(348, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 32),
(349, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 33),
(350, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 33),
(351, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 33),
(352, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 33),
(353, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 33),
(354, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 33),
(355, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 33),
(356, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 33),
(357, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 33),
(358, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 33),
(359, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 33),
(360, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 33),
(361, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 33),
(362, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 33),
(363, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 33),
(364, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 33),
(365, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 34),
(366, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 34),
(367, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 34),
(368, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 34),
(369, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 34),
(370, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 34),
(371, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 34),
(372, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 34),
(373, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 34),
(374, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 34),
(375, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 34),
(376, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 34),
(377, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 34),
(378, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 34),
(379, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 34),
(380, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 35),
(381, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 35),
(382, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 35),
(383, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 35),
(384, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 35),
(385, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 35),
(386, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 35),
(387, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 35),
(388, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 35),
(389, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 35),
(390, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 35),
(391, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 35),
(392, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 37),
(393, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 37),
(394, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 37),
(395, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 37),
(396, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 37),
(397, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 37),
(398, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 37),
(399, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(400, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(401, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(402, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(403, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(404, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(405, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 38),
(406, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(407, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(408, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(409, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(410, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 38),
(411, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(412, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(413, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(414, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(415, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(416, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(417, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(418, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 38),
(419, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 38),
(420, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 39),
(421, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 39),
(422, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 39),
(423, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 39),
(424, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 39),
(425, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 39),
(426, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 39),
(427, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 39),
(428, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 39),
(429, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 39),
(430, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 39),
(431, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 39),
(432, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(433, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(434, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 41),
(435, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(436, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(437, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(438, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 41),
(439, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 41),
(440, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(441, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 41),
(442, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 41),
(443, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 41),
(444, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(445, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 41),
(446, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 41),
(447, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 41),
(448, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 41),
(449, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 41),
(450, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 41),
(451, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 41),
(452, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 42),
(453, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 42),
(454, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 42),
(455, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 42),
(456, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 44),
(457, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 44),
(458, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 44),
(459, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 44),
(460, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 44),
(461, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 44),
(462, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 44),
(463, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 44),
(464, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 44),
(465, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 45),
(466, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 45),
(467, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 45),
(468, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 46),
(469, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 46),
(470, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 46),
(471, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 46),
(472, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 46),
(473, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 46),
(474, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 46),
(475, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 46),
(476, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 46),
(477, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 46),
(478, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 46),
(479, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 47),
(480, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 47),
(481, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 48),
(482, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 48),
(483, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 48),
(484, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 48),
(485, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 48),
(486, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 48),
(487, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 49),
(488, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 49),
(489, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 49),
(490, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 49),
(491, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 49),
(492, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 49),
(493, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 49),
(494, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 49),
(495, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 49),
(496, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 49),
(497, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 49),
(498, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 49),
(499, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 49),
(500, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 50),
(501, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(502, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(503, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 50),
(504, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(505, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 50),
(506, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 50),
(507, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 50),
(508, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(509, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(510, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 50),
(511, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 50),
(512, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 50),
(513, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(514, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 50),
(515, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(516, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 50),
(517, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 50),
(518, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(519, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 50),
(520, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 50),
(521, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 51),
(522, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 51),
(523, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 51),
(524, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 51),
(525, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 51),
(526, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 51),
(527, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 51),
(528, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 51),
(529, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 51),
(530, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 51),
(531, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 51),
(532, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 52),
(533, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 52),
(534, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 52),
(535, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 52),
(536, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 52),
(537, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 52),
(538, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 52),
(539, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 52),
(540, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(541, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 53),
(542, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(543, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 53),
(544, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(545, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(546, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(547, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 53),
(548, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(549, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 53),
(550, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(551, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 53),
(552, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(553, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 53),
(554, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 53),
(555, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 53),
(556, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 54),
(557, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 54),
(558, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 54),
(559, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 54),
(560, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 54),
(561, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 54),
(562, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 54),
(563, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 54),
(564, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 54),
(565, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 54),
(566, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 54),
(567, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 54),
(568, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 55),
(569, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 55),
(570, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 55),
(571, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 55),
(572, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 55),
(573, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 55),
(574, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 55),
(575, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 55),
(576, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 55),
(577, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 55),
(578, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 55),
(579, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 55),
(580, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 55),
(581, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 56),
(582, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 56),
(583, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 56),
(584, '2021-06-25', '/media/album/common/1619788949530zb3vrUgbtyBb.jpg', 0, 56),
(585, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 56),
(586, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 56),
(587, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 56),
(588, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 56),
(589, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 56),
(590, '2021-06-25', '/media/album/common/1619789224979BYakIw8GSYR4.jpg', 0, 56),
(591, '2021-06-25', '/media/album/common/1619789224952qv4J5dLfI5rI.jpg', 0, 56);

-- --------------------------------------------------------

--
-- Table structure for table `role`
--

CREATE TABLE `role` (
  `id` bigint NOT NULL,
  `name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `role`
--

INSERT INTO `role` (`id`, `name`) VALUES
(1, 'ROLE_USER'),
(2, 'ROLE_ADMIN');

-- --------------------------------------------------------

--
-- Table structure for table `user`
--

CREATE TABLE `user` (
  `id` bigint NOT NULL,
  `created_at` date DEFAULT NULL,
  `account_expired` tinyint(1) NOT NULL DEFAULT '0',
  `auth_token` varchar(510) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `credentials_expired` tinyint(1) NOT NULL DEFAULT '0',
  `email` varchar(255) NOT NULL,
  `email_token` varchar(510) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `enabled` tinyint(1) NOT NULL DEFAULT '0',
  `locked` tinyint(1) NOT NULL DEFAULT '0',
  `password` varchar(255) NOT NULL,
  `password_token` varchar(510) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `username` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `user`
--

INSERT INTO `user` (`id`, `created_at`, `account_expired`, `auth_token`, `credentials_expired`, `email`, `email_token`, `enabled`, `locked`, `password`, `password_token`, `username`) VALUES
(1, '2021-06-17', 0, NULL, 0, '<EMAIL>', NULL, 1, 0, '$2a$10$NRxYSRSPsHrqsqO8MrbmJ.pEMG6g.F8WvUBN2ZVPvWFhGvnfWjUxm', NULL, 'Raina'),
(2, '2021-06-17', 0, NULL, 0, '<EMAIL>', NULL, 1, 0, '$2a$10$7np8eohUED/R8ed7ijESfe2MTvopJSkebBl5u3CMLK5pnXle.4coy', NULL, 'Solomon'),
(3, '2021-06-17', 0, NULL, 0, '<EMAIL>', NULL, 1, 0, '$2a$10$u/jliGTMdmSDZgL0Xr83J.MWPxDfGhKT3oDf478OTyR3k688MqZlG', NULL, 'Elaine'),
(4, '2021-06-17', 0, NULL, 0, '<EMAIL>', NULL, 1, 0, '$2a$10$S5B1WzC8H206cbROUdubYO/Dg9D0n5ylpkXNTIgFXlSWrA77m6yWS', NULL, 'Dominick');

-- --------------------------------------------------------

--
-- Table structure for table `user_data`
--

CREATE TABLE `user_data` (
  `last_login` date DEFAULT NULL,
  `last_update_check` date DEFAULT NULL,
  `user_id` bigint NOT NULL,
  `theme` tinyint DEFAULT '1',
  `last_update_check_sort` date DEFAULT NULL COMMENT 'date to be used when user checked updates and user uses sort to group updates (today, last week, ...)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `user_data`
--

INSERT INTO `user_data` (`last_login`, `last_update_check`, `user_id`, `theme`, `last_update_check_sort`) VALUES
(NULL, '2021-07-22', 1, 2, '2021-07-05'),
(NULL, NULL, 2, 1, NULL),
(NULL, NULL, 3, 1, NULL),
(NULL, NULL, 4, 1, NULL);

-- --------------------------------------------------------

--
-- Table structure for table `user_roles`
--

CREATE TABLE `user_roles` (
  `user_id` bigint NOT NULL,
  `roles_id` bigint NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `user_roles`
--

INSERT INTO `user_roles` (`user_id`, `roles_id`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1);

-- --------------------------------------------------------

--
-- Table structure for table `user_subscribers`
--

CREATE TABLE `user_subscribers` (
  `user_id` bigint NOT NULL,
  `subscribers_id` bigint NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dumping data for table `user_subscribers`
--

INSERT INTO `user_subscribers` (`user_id`, `subscribers_id`) VALUES
(2, 1),
(4, 1),
(2, 4);

--
-- Indexes for dumped tables
--

--
-- Indexes for table `album`
--
ALTER TABLE `album`
  ADD PRIMARY KEY (`id`),
  ADD KEY `FKmi5m81x9aswan1ci0wnw04dq1` (`user_id`);

--
-- Indexes for table `image`
--
ALTER TABLE `image`
  ADD PRIMARY KEY (`id`),
  ADD KEY `FKklgd5pxhpuh3nwik115myord` (`album_id`);

--
-- Indexes for table `role`
--
ALTER TABLE `role`
  ADD PRIMARY KEY (`id`);

--
-- Indexes for table `user`
--
ALTER TABLE `user`
  ADD PRIMARY KEY (`id`);

--
-- Indexes for table `user_data`
--
ALTER TABLE `user_data`
  ADD PRIMARY KEY (`user_id`);

--
-- Indexes for table `user_roles`
--
ALTER TABLE `user_roles`
  ADD PRIMARY KEY (`user_id`,`roles_id`),
  ADD KEY `FKj9553ass9uctjrmh0gkqsmv0d` (`roles_id`);

--
-- Indexes for table `user_subscribers`
--
ALTER TABLE `user_subscribers`
  ADD PRIMARY KEY (`user_id`,`subscribers_id`),
  ADD KEY `FKsic6kr4t25xyvr8hobrpyehip` (`subscribers_id`);

--
-- AUTO_INCREMENT for dumped tables
--

--
-- AUTO_INCREMENT for table `album`
--
ALTER TABLE `album`
  MODIFY `id` bigint NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=62;

--
-- AUTO_INCREMENT for table `image`
--
ALTER TABLE `image`
  MODIFY `id` bigint NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=616;

--
-- AUTO_INCREMENT for table `role`
--
ALTER TABLE `role`
  MODIFY `id` bigint NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;

--
-- AUTO_INCREMENT for table `user`
--
ALTER TABLE `user`
  MODIFY `id` bigint NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=19;

--
-- Constraints for dumped tables
--

--
-- Constraints for table `album`
--
ALTER TABLE `album`
  ADD CONSTRAINT `FKmi5m81x9aswan1ci0wnw04dq1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`);

--
-- Constraints for table `image`
--
ALTER TABLE `image`
  ADD CONSTRAINT `FKklgd5pxhpuh3nwik115myord` FOREIGN KEY (`album_id`) REFERENCES `album` (`id`);

--
-- Constraints for table `user_data`
--
ALTER TABLE `user_data`
  ADD CONSTRAINT `FKelmpammgqm7p72tyao7p2vb00` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`);

--
-- Constraints for table `user_roles`
--
ALTER TABLE `user_roles`
  ADD CONSTRAINT `FK55itppkw3i07do3h7qoclqd4k` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  ADD CONSTRAINT `FKj9553ass9uctjrmh0gkqsmv0d` FOREIGN KEY (`roles_id`) REFERENCES `role` (`id`);

--
-- Constraints for table `user_subscribers`
--
ALTER TABLE `user_subscribers`
  ADD CONSTRAINT `FKbygtkeaaeadsse09tt411m1eh` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  ADD CONSTRAINT `FKsic6kr4t25xyvr8hobrpyehip` FOREIGN KEY (`subscribers_id`) REFERENCES `user` (`id`);
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
<file_sep>/src/main/java/com/gallery/model/entity/AbstractEntity.java
package com.gallery.model.entity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import javax.persistence.*;
import java.util.Date;
import java.util.Objects;

//@ApiModel(subTypes = {User.class, Album.class, Image.class})
@Getter
@ToString
@MappedSuperclass
public abstract class AbstractEntity {

//    @ApiModelProperty(name = "id", dataType = "Long", example = "1", required = true)
    @Setter
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;

//    @ApiModelProperty(name = "creation datetime", dataType = "Date", allowEmptyValue = true, position = 1)
    @Setter
    @CreationTimestamp
    @Temporal(value = TemporalType.DATE)
    @Column(name = "created_at", updatable = false)
    protected Date createdAt;

    @PrePersist
    private void prePersist() {
        createdAt = new Date();
    }

    @Override
    public boolean equals(Object o) {
//        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AbstractEntity abstractEntity = (AbstractEntity) o;
        if (abstractEntity.getId() == null && this.getId() == null) {
            return this == abstractEntity;
        }
        return Objects.equals(this.getId(), abstractEntity.getId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.getId());
    }
}
<file_sep>/src/main/java/com/gallery/util/TokenGenerator.java
package com.gallery.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gallery.model.dto.user.UserDto;
import io.jsonwebtoken.*;
import net.bytebuddy.utility.RandomString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

@Component
public class TokenGenerator {

    @Value("${jwt.key.secret}")
    private String secret;

    @Value("${request.change.email.token.seconds}")
    private Long emailChangeRequestExpiration;

    @Value("${request.change.password.token.seconds}")
    private Long passwordChangeRequestExpiration;

    public boolean isTokenValid(String token) {
        if (token == null || token.isEmpty()) {
            return false;
        }
        Date tokenDate;
        try {
            tokenDate = new Date(extractTokenTime(token));
        }
        catch (NumberFormatException e) {
            e.printStackTrace();
            tokenDate = new Date(0);
        }
        Date nowDate = Calendar.getInstance().getTime();
        System.out.println("nowDate = " + nowDate);
        System.out.println("tokenDate = " + tokenDate);
        return nowDate.before(tokenDate);
    }

    /**
     * @param token email or password change request token
     * @return  object array with length of 2:<br/>[0] - UserDto object,<br/>[1] - expiration date.
     * @throws SignatureException
     */
    public Object[] getChangeRequestTokenData(String token) throws SignatureException, ExpiredJwtException, JsonProcessingException {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        String subject = claims.getSubject();
        Date expiration = claims.getExpiration();
        Object[] data = new Object[2];
        ObjectMapper objectMapper = new ObjectMapper();
        data[0] = objectMapper.readValue(subject, UserDto.class);
        data[1] = expiration;
        return data;
    }

    public long extractTokenTime(String token) {
        int startIndex = token.lastIndexOf('_');
        String time = token.substring(startIndex + 1);
        Long timeMillis = Long.parseLong(time);
        return timeMillis;
    }

    public String generateEmailToken(UserDto userDto) throws JsonProcessingException {
        return generateRequestToken(userDto, emailChangeRequestExpiration);
    }

    public String generatePasswordToken(UserDto userDto) throws JsonProcessingException {
        return generateRequestToken(userDto, passwordChangeRequestExpiration);
    }

    public String generateRequestToken(UserDto userDto, long expirationSec) throws JsonProcessingException {
        Date dateNow = new Date();
        String subject = new ObjectMapper().writeValueAsString(userDto);
        JwtBuilder jwtBuilder = Jwts.builder()
                .setClaims(new HashMap<>())
                .setSubject(subject)
                .setIssuedAt(dateNow)
                .setExpiration(new Date(dateNow.getTime() + (expirationSec * 1000)))
                .signWith(SignatureAlgorithm.HS256, secret);
        return jwtBuilder.compact();
    }

    public String generateSimpleToken(int length) {
        if (length <= 5) {
            throw new IllegalArgumentException("parameter \"length\" must be positive and greater than 5!");
        }
//        String token = new RandomString(length).nextString() + "_" + (System.currentTimeMillis() + AppConfig.TOKEN_LIFETIME_MILLIS);;
        return new RandomString(length).nextString() + "_" + (System.currentTimeMillis() + (passwordChangeRequestExpiration * 1000));
//        try {
//            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
//            messageDigest.update(token.getBytes());
//            token = new String(messageDigest.digest());
//        }catch (NoSuchAlgorithmException e){
//            System.out.println("NoSuchAlgorithmException");
//        }
//        return token;
    }

}
<file_sep>/src/test/java/com/gallery/service/RoleRepositoryTest.java
package com.gallery.service;

import com.gallery.model.entity.user.Role;
import com.gallery.model.entity.user.User;
import com.gallery.repository.RoleRepository;
import com.gallery.service.common.ServiceTest;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;

public class RoleRepositoryTest implements ServiceTest {

    private static Logger logger = LoggerFactory.getLogger(RoleRepositoryTest.class);

    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    private UserService userService;

    @Test
    public void saveAdminRole() {
        long rolesCountBefore = roleRepository.count();

        Role adminRole = new Role(Role.ROLE_ADMIN_VALUE);
        Role savedAdminRole = roleRepository.save(adminRole);


        long rolesCountAfter = roleRepository.count();

        Assert.assertNotNull(savedAdminRole);
        Assert.assertEquals("Assert Roles Count.", (rolesCountBefore + 1), rolesCountAfter);
    }

    @Test
    public void deleteUserRole() {
        long rolesCountBefore = roleRepository.count();

        List<User> userList = (List<User>) userService.findAll();
        for (User u : userList){
            u.getRoles().clear();
        }
        userService.getUserRepository().saveAll(userList);

        Role role = new Role(Role.ROLE_USER_VALUE);
        role.setId(1L);
        roleRepository.deleteById(role.getId());

        long rolesCountAfter = roleRepository.count();
        Assert.assertEquals("Assert Roles Count.", (rolesCountBefore - 1), rolesCountAfter);
    }

//    @Before public void onSetup() {}
//    @After public void onTearDown() {}

}
<file_sep>/src/main/java/com/gallery/model/ApplicationProperties.java
package com.gallery.model;

import org.springframework.core.env.Environment;
import java.util.HashMap;
import java.util.Map;

//@Component(value = "applicationProperties")
//@PropertySource(value = {"classpath:application.properties"})
public class ApplicationProperties {

    private Environment environment;

    //    @Autowired
    public ApplicationProperties(Environment environment) {
        this.environment = environment;
    }

    public String getProperty(String name) {
        return this.environment.getProperty(name);
    }

    public Map<String, String> getH2ConfigProperties() {
        Map<String, String> properties = new HashMap<>();
        properties.put("connection.dialect", environment.getProperty("spring.jpa.database-platform"));
        properties.put("hibernate.connection.url", environment.getProperty("spring.datasource.url"));
        properties.put("hibernate.connection.username", environment.getProperty("spring.datasource.username"));
        properties.put("hibernate.connection.password", environment.getProperty("spring.datasource.password"));
        properties.put("hibernate.current_session_context_class", environment.getProperty("hibernate.current_session_context_class"));

        return properties;
    }
}
<file_sep>/src/test/java/com/gallery/service/common/ServiceTest.java
package com.gallery.service.common;

import com.gallery.AppTest;
import com.gallery.service.EntityService;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;

//@ActiveProfiles("test")
//@SpringBootTest(classes = {App.class, TestHibernateConfiguration.class})
//@SpringBootTest(classes = {App.class})

@Transactional
@Rollback
public interface ServiceTest extends AppTest {

    default Long count(EntityService entityService){
        return entityService.count();
    }

}
<file_sep>/src/main/resources/hidden_hibernate.properties
#connection.driver_class=com.mysql.jdbc.Driver
#hibernate.connection.driver_class=com.mysql.jdbc.Driver
#hibernate.dialect=org.hibernate.dialect.MySQLDialect
#hibernate.connection.username=root
#hibernate.connection.password=<PASSWORD>
#hibernate.connection.url=jdbc:mysql://localhost/com.gallery?serverTimezone=UTC
#hibernate.hbm2ddl=update
#hibernate.show_sql=true
#hibernate.format_sql=true<file_sep>/src/main/java/com/gallery/web/PageController.java
package com.gallery.web;


import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PageController {


    @GetMapping({"/", "/index"})
    public String index(@AuthenticationPrincipal User user) {
        String token = null;
        return "page/index";
    }

    @GetMapping("/welcome")
    public String welcome(@AuthenticationPrincipal User user) {
        SecurityContext securityContextHolder = SecurityContextHolder.getContext();
        System.out.println("PRINCIPAL>>>>> " + user);
        return "page/welcome";
    }
}
<file_sep>/src/main/java/com/gallery/service/ImageService.java
package com.gallery.service;

import com.gallery.model.entity.album.Image;
import com.gallery.repository.ImageRepository;
import com.gallery.util.FunctionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.Collection;
import java.util.Optional;

@Service
public class ImageService implements EntityService {

    @PersistenceContext
    private EntityManager entityManager;

    private ImageRepository imageRepository;

    @Autowired
    public ImageService(ImageRepository imageRepository) {
        this.imageRepository = imageRepository;
    }

    public Image save(Image image) {
        return imageRepository.save(image);
    }

    public Iterable<Image> findAll() {
        return imageRepository.findAll();
    }

    public Optional<Image> findById(Long id){
        return imageRepository.findById(id);
    }

    public Collection<Image> findAllByAlbumId(Long albumId) {
        return imageRepository.findAllByAlbum(albumId);
    }

    @Override
    public long count() {
        return imageRepository.count();
    }
    public void saveAll(Iterable<Image> entites) {
        imageRepository.saveAll(entites);
    }

    public void delete(Long id) {
        imageRepository.deleteById(id);
    }

    public void delete(Image entity) {
        imageRepository.delete(entity);
    }

    public Collection<Image> findAll(Long albumId, Integer offset, Integer limit) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Image> imageCriteriaQuery = cb.createQuery(Image.class);

        Root<Image> imageRoot = imageCriteriaQuery.from(Image.class);

        //SELECT case with JUNCTION TABLE (works)
/*        Root<Album> albumRoot = imageCriteriaQuery.from(Album.class);
        Join<Album, Image> albumImageJoin = albumRoot.join(Album_.images, JoinType.LEFT);
        imageCriteriaQuery.select(albumImageJoin)
        .where( cb.equal(albumRoot.get(Album_.id), albumId));*/

        //SELECT case with join column
        // i.e. adding @JoinColumn for Collection<Image> images in album entity class
        // (does not work - return duplicates for each album)
/*        Root<Album> albumRoot = imageCriteriaQuery.from(Album.class);
        Join<Album, Image> albumImageJoin = albumRoot.join(Album_.IMAGES, JoinType.LEFT);
        imageCriteriaQuery.select(albumImageJoin).where(cb.equal(albumRoot.get(Album_.ID), albumId));*/

//        TypedQuery<Image> typedQuery = entityManager.createQuery(imageCriteriaQuery);

        //SELECT case with query
        TypedQuery<Image> query = entityManager
                .createQuery("SELECT img FROM Album a inner join a.images img WHERE a.id=:id ORDER BY img.createdAt DESC", Image.class)
                .setParameter("id", albumId);

        FunctionUtil.applyOffsetAndLimit(query, offset, limit);
        System.out.println(query.getResultList().getClass().getName());

        return query.getResultList();
    }
}
<file_sep>/src/test/java/com/gallery/AppTest.java
package com.gallery;

import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * Entry point for test classes.
 * */
//@RunWith(SpringRunner.class)
//@DataJpaTest
//@WebAppConfiguration
//@AutoConfigureTestEntityManager
//@EnableJpaRepositories(basePackages = {"com.gallery.repository"})
//@ComponentScan(basePackages = {"com.gallery.model.entity", "com.gallery.respository", "com.gallery.service"})
//@ComponentScan(basePackages = {"com.gallery"})
//@SpringBootTest(classes = {App.class, TestHibernateConfiguration.class})
//@ActiveProfiles("test")
//@PropertySource("classpath:application-test.properties")
//@ContextConfiguration(classes = {BCrypConfig.class})
//@ContextConfiguration(classes = {App.class})
//@TestPropertySource("classpath:application-test.properties")
//@Transactional

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@SpringBootTest(classes = {App.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public interface AppTest {
}
<file_sep>/src/main/java/com/gallery/service/delayedtask/AbstractDelayedEntityRemover.java
package com.gallery.service.delayedtask;

import com.gallery.model.entity.AbstractEntity;
import org.springframework.data.repository.CrudRepository;
import java.util.Queue;


public abstract class AbstractDelayedEntityRemover<T extends AbstractEntity> implements DelayedEntityRemover<T> {

    protected CrudRepository<T, Long> repository;
    protected boolean running = false;

    public AbstractDelayedEntityRemover(CrudRepository<T, Long> repository) {
        this.repository = repository;
    }

    public void run() {
        new Thread( () -> {
            Queue<TaskItem<T>> items = getItems();
            while ( ! items.isEmpty()) {
                try {
                    Thread.sleep(TaskItem.SLEEP_TIME_MILLIS);
                    while ( ! items.isEmpty() &&  (items.peek().getLifeTimeMillis() <= System.currentTimeMillis()) ) {
                        TaskItem<T> item = items.peek();
                        removeEntity(item.getValue());
                    }
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            running = false;
        }).start();
    }

}
<file_sep>/src/test/java/com/gallery/Persister.java
package com.gallery;

import com.gallery.model.entity.album.Album;
import com.gallery.model.entity.album.Image;
import com.gallery.model.entity.user.Role;
import com.gallery.model.entity.user.User;
import com.gallery.repository.RoleRepository;
import com.gallery.service.AlbumService;
import com.gallery.service.UserService;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import javax.management.relation.RoleNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

public class Persister implements AppTest {

    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private UserService userService;
    @Autowired
    private AlbumService albumService;

    public static List<User> USERS = new ArrayList<>();
    static {
        USERS.addAll(
                List.of(
                        // users to be persisted into test database
                        new User("Raina", "raina_mercer", "<EMAIL>"),
                        new User("Solomon", "solomon_rosales", "<EMAIL>"),
                        new User("Elaine", "elaine_sexton", "<EMAIL>"),
                        new User("Dominick", "dominick_garza", "<EMAIL>")
                )
        );
    }

    @Test
    public void persistAlbums() {
        User user1 = userService.findByUsernameWithAlbums("Raina"); // has 16 albums | subscribed on Solomon and Dominick
        User user2 = userService.findByUsernameWithAlbums("Solomon"); // has 18 albums
        User user4 = userService.findByUsernameWithAlbums("Dominick"); // has 22 albums | subscribed on Solomon
        if( ! user1.getAlbums().isEmpty()) {
            return;
        }
        Random random = new Random();
        int rand = 0;
        for (int i = 1; i <= 56; i++) {
            Album album = new Album("album #" + i);
            rand = random.nextInt(22);
            if (i <= 16) {
                for (int j = 0; j < rand; j++) {
                    album.getImages().add(new Image("image" + i));
                }
                user1.addAlbum(album);
            }
            else if (i >= 39) {
                for (int j = 0; j < rand; j++) {
                    album.getImages().add(new Image("image" + i));
                }
                user2.addAlbum(album);
            }
            else {
                for (int j = 0; j < rand; j++) {
                    album.getImages().add(new Image("image" + i));
                }
                user4.addAlbum(album);
            }
        }
        userService.save(user1);
        userService.save(user2);
        userService.save(user4);
    }

    @Test
    public void persistUsers() {
        Collection<User> users = (Collection<User>) userService.findAll();
        if(users.size() == 0){
            Exception exception = null;
            try {
                for (User u : USERS) {
                    userService.saveNew(u);
                }
            }
            catch (RoleNotFoundException e){
                System.err.println("Consider insert USER_ROLE entity first.");
                exception = e;
                exception.printStackTrace();;
            }
            finally {
                Assert.assertNull(exception);
            }
        }
    }

    public void persistRoles(){
        roleRepository.save(new Role(Role.ROLE_USER_VALUE));
        roleRepository.save(new Role(Role.ROLE_ADMIN_VALUE));
    }

/*
user_id > albumd_id > images_count
    |1|1|9
    |1|2|8
    |1|3|7
    |1|4|18
    |1|5|17
    |1|6|11
    |1|7|3
    |1|8|3
    |1|9|13
    |1|10|16
    |1|11|5
    |1|12|10
    |1|13|9
    |1|14|19
    |1|15|5
    |1|16|20
    |2|17|12
    |2|18|6
    |2|19|8
    |2|20|21
    |2|21|16
    |2|22|15
    |2|23|14
    |2|24|1
    |2|25|8
    |2|26|6
    |2|27|2
    |2|28|20
    |2|29|2
    |2|30|3
    |2|31|20
    |2|32|21
    |2|33|16
    |2|34|15
    |4|35|12
    |4|37|7
    |4|38|21
    |4|39|12
    |4|41|20
    |4|42|4
    |4|44|9
    |4|45|3
    |4|46|11
    |4|47|2
    |4|48|6
    |4|49|13
    |4|50|21
    |4|51|11
    |4|52|8
    |4|53|16
    |4|54|12
    |4|55|13
    |4|56|11
*/
}
<file_sep>/src/main/java/com/gallery/model/entity/album/Album_.java
package com.gallery.model.entity.album;

import com.gallery.model.entity.AbstractEntity_;
import com.gallery.model.entity.user.User;
import javax.annotation.Generated;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Album.class)
public class Album_ extends AbstractEntity_ {

	public static volatile SingularAttribute<Album, String> cover;
	public static volatile ListAttribute<Album, Image> images;
	public static volatile SingularAttribute<Album, Boolean> deleted;
	public static volatile SingularAttribute<Album, String> description;
	public static volatile SingularAttribute<Album, String> title;
	public static volatile SingularAttribute<Album, User> user;

	public static final String COVER = "cover";
	public static final String IMAGES = "images";
	public static final String DELETED = "deleted";
	public static final String DESCRIPTION = "description";
	public static final String TITLE = "title";
	public static final String USER = "user";

}

<file_sep>/src/main/java/com/gallery/config/JmsConfiguration.java
package com.gallery.config;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.core.JmsTemplate;
import javax.jms.ConnectionFactory;


@Configuration
public class JmsConfiguration {

    @Value("${spring.activemq.broker-url}")
    private String borkerUrl;

    @Value("${spring.activemq.user}")
    private String username;

    @Value("${spring.activemq.password}")
    private String password;

    @Bean
    public ConnectionFactory connectionFactory() {
        return new ActiveMQConnectionFactory(username, password, borkerUrl);
    }

    @Bean
    public JmsTemplate jmsTemplate() {
        JmsTemplate template = new JmsTemplate();
        template.setConnectionFactory(connectionFactory());
        return template;
    }

    /*@Bean
  public MessageConverter messageConverter(){
      return new Jackson2JsonMessageConverter();
  }*/

   /* @Bean
    public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
                                                    DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        // This provides all boot's default to this factory, including the message converter
        configurer.configure(factory, connectionFactory);
        // You could still override some of Boot's default if necessary.
        return factory;
    }

    @Bean // Serialize message content to json using TextMessage
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
        return converter;
    }*/

}
<file_sep>/src/main/java/com/gallery/jwt/JwtEntryPoint.java
package com.gallery.jwt;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//@Component
public class JwtEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Jwt token not provided.");
    }
}
<file_sep>/src/main/java/com/gallery/service/entity/Fetchable.java
package com.gallery.service.entity;

import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Root;

public interface Fetchable {

    default <T> void fetch(Root<T> root, String[] fetchOptions) {
        for (String option : fetchOptions) {
            try {
                root.fetch(option, JoinType.LEFT);
            } catch (IllegalArgumentException ignored) {
                System.err.println("Illegal argument option: " + option);
            }
        }
    }
}
<file_sep>/src/main/java/com/gallery/util/image/ImageSaver.java
package com.gallery.util.image;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

@AllArgsConstructor
@Getter
public class ImageSaver {

    public static String ROOT_CATALOG = "/media";
    public static final String SMALL_IMAGE_PREFIX = "sm";
    public static final int SMALL_IMAGE_MIN_WIDTH = 200;
    public static final int SMALL_IMAGE_MAX_WIDTH = 280;
    public static final int SMALL_IMAGE_MIN_HEIGHT = 200;
    public static final int SMALL_IMAGE_MAX_HEIGHT = 360;

    public String saveSourceImage(MultipartFile multipartFile, String destImagePath) throws IOException {
        if (this.save(multipartFile, destImagePath)) {
            return destImagePath;
        }
        return null;
    }

    public String saveSmallImage(MultipartFile multipartFile, String smallImageDestPath, String extensionNoDot) throws IOException {
        return saveSmallImage(ImageIO.read(multipartFile.getInputStream()), smallImageDestPath, extensionNoDot);
    }

    public String saveSmallImage(File sourceImageFile, String smallImageDestPath, String extensionNoDot) throws IOException {
        return saveSmallImage(ImageIO.read(sourceImageFile), smallImageDestPath, extensionNoDot);
    }

    private boolean save(MultipartFile multipartFile, String path) throws IOException {
        int res = FileCopyUtils.copy(multipartFile.getInputStream(), Files.newOutputStream(Path.of(path)));
        return res > 0;
    }

    public String saveSmallImage(BufferedImage originalImage, String destinationPath, String extensionNoDot) throws IOException{
        float sizeCoefficient = this.getSizeCoefficient(originalImage.getWidth(), originalImage.getHeight());

        int width = (int) (originalImage.getWidth() * sizeCoefficient);
        int height = (int) (originalImage.getHeight() * sizeCoefficient);
        int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();

        BufferedImage miniImage = new BufferedImage(width, height, type);

        Graphics2D graphics2D = miniImage.createGraphics();
        graphics2D.drawImage(originalImage, 0, 0, width, height, null);
        graphics2D.dispose();

        if (ImageIO.write(miniImage, extensionNoDot, new File(destinationPath))) {
            return destinationPath;
        }

        return null;
    }

    private float getSizeCoefficient(int width, int height) {
        if (width <= SMALL_IMAGE_MIN_WIDTH && height <= SMALL_IMAGE_MIN_HEIGHT) {
            return 1.0f;
        }
        else {
            float coefficient = 0.98f;
            float decrementStep = .02f;
            float res = width * coefficient;
            while (res > SMALL_IMAGE_MAX_WIDTH) {
                res = width * coefficient;
                coefficient = coefficient - decrementStep;
            }
            return coefficient;
        }
    }
}
<file_sep>/src/main/java/com/gallery/jms/AlbumMessagingService.java
package com.gallery.jms;

//import org.springframework.amqp.rabbit.core.RabbitTemplate;
//import org.springframework.amqp.support.converter.MessageConverter;
//import org.springframework.stereotype.Service;

//@Service
public class AlbumMessagingService
//        implements MessageingService<Album>
{

    /*@Value("${amqp.routing-key.album}")
    private String routingKey;

    private RabbitTemplate rabbitTemplate;
    private MessageConverter messageConverter;

    @Autowired
    public AlbumMessagingService(RabbitTemplate rabbitTemplate, MessageConverter messageConverter) {
        this.rabbitTemplate = rabbitTemplate;
        this.messageConverter = messageConverter;
    }

    @Override
    public void send(Album album) {
//        MessageProperties mp = new MessageProperties();
//        Message message = messageConverter.toMessage(album, mp);
//        rabbitTemplate.send(message);
        rabbitTemplate.convertAndSend(routingKey, album);
    }*/

}
<file_sep>/src/main/java/com/gallery/web/ImageController.java
package com.gallery.web;

import com.gallery.model.dto.PaginationData;
import com.gallery.model.dto.album.AlbumDto;
import com.gallery.model.entity.album.Album;
import com.gallery.model.entity.album.Album_;
import com.gallery.model.entity.album.Image;
import com.gallery.model.entity.user.User;
import com.gallery.service.AlbumService;
import com.gallery.service.ImageService;
import com.gallery.service.entity.SingleEntityServiceImpl;
import com.gallery.util.FunctionUtil;
import com.gallery.util.ResponseModel;
import com.gallery.util.factory.MultipartFileImageFactoryImpl;
import com.gallery.util.image.MultipartFileImage;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

@RestController
@RequestMapping("/image")
public class ImageController {

    private static Logger logger = LoggerFactory.getLogger(ImageController.class);

    @Value("${pagination.page-items-size}")
    private int pageItemsSize;

    private AlbumService albumService;
    private ImageService imageService;
    private MultipartFileImageFactoryImpl multipartFileImageFactory;
    private SingleEntityServiceImpl entityService;

    @Autowired
    public ImageController(AlbumService albumService, ImageService imageService, MultipartFileImageFactoryImpl multipartFileImageFactory, SingleEntityServiceImpl entityService) {
        this.albumService = albumService;
        this.imageService = imageService;
        this.entityService = entityService;
        this.multipartFileImageFactory = multipartFileImageFactory;
    }

    /**
     * Get images by album id.
     * */
    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "get images by ablum")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "page"),
            @ApiImplicitParam(name = "offset"),
            @ApiImplicitParam(name = "limit"),
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "get images"),
            @ApiResponse(code = 404, message = "album not found")
    })
    @GetMapping("/{albumId}/all")
    public ResponseModel albumImages(@PathVariable Long albumId,
                                     @ApiParam("page") @RequestParam(value = "page", defaultValue = "0", required = false) Integer page,
                                     @ApiParam("offset") @RequestParam(value = "offset", defaultValue = "0", required = false) Integer offset,
                                     @ApiParam("limit") @RequestParam(value = "limit", required = false) Integer limit) {
        logger.info("get images by album");
        ResponseModel responseModel = new ResponseModel();

        offset = FunctionUtil.resolveOffset(offset, page, pageItemsSize);
        limit = FunctionUtil.resolveLimit(limit, pageItemsSize);

        AlbumDto album = albumService.findOneAndConstructAlbumDto(FunctionUtil.getWhereClauseEntry("album", "id", albumId.toString()));
        if (album != null) {
            Collection<Image> images = imageService.findAll(album.getId(), offset, limit);
            PaginationData paginationData = new PaginationData(offset, page, album.getImagesCount(), images.size(), pageItemsSize);
            responseModel.addAttribute(ResponseModel.ATTR_ALBUM, album);
            responseModel.addAttribute(ResponseModel.ATTR_IMAGES, images);
            responseModel.addAttribute(ResponseModel.ATTR_PAGINATION, paginationData);
        }
        else {
            responseModel.setStatus(HttpStatus.NOT_FOUND);
        }

        return responseModel;
    }

    @ResponseStatus(HttpStatus.CREATED)
    @ApiOperation(value = "upload images to album")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "images array"),
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "images uploaded successfully"),
            @ApiResponse(code = 500, message = "internal server error (exception)")
    })
    //upload images
    @PostMapping("/{albumId}/upload")
    public ResponseModel albumImagesAdd(@ApiIgnore @AuthenticationPrincipal User principal,
                                        @ApiIgnore @PathVariable Long albumId,
                                        @ApiParam("images array (multiple)") @RequestParam("images") MultipartFile[] images) {
        logger.info("UPLOAD IMAGES");
        ResponseModel responseModel = new ResponseModel(HttpStatus.CREATED);

        Album album = entityService.findOneByAttrWithOptions(Album.class, Album_.ID, String.valueOf(albumId), Album_.USER, Album_.IMAGES);

        if (album == null || !(album.isHost(principal))) {
            return new ResponseModel(HttpStatus.NOT_FOUND);
        }

        List<Image> imagesList = new ArrayList<>();
        try {
            for (MultipartFile image : images) {
                String endPath = FunctionUtil.buildImagePath(album.getUser().getUsername(), album.getId());
                MultipartFileImage multipartFileImage = multipartFileImageFactory.create(endPath);
                String savedImagePath = multipartFileImage.saveImages(image);
                Image imageEntity = new Image(savedImagePath);
                logger.info(imageEntity.toString());
                imagesList.add(imageEntity);
            }
        }
        catch (IOException e) {
            responseModel.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
            e.printStackTrace();
        }

        int length = album.getImages().size();
        album.getImages().addAll(imagesList);
        albumService.save(album);

        return responseModel;
    }


    @ResponseStatus(HttpStatus.OK)
    @ApiOperation(value = "delete image")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "get images"),
            @ApiResponse(code = 500, message = "internal server error (exception)")
    })
    //    @DeleteMapping("/{id:\\d+}/delete")
//    public void deleteImage(@PathVariable("id") Long imageId) {
    @DeleteMapping("/{albumId}/delete/{imageId}")
    public ResponseModel deleteImage(@ApiIgnore @AuthenticationPrincipal User principal,
                                     @ApiIgnore @PathVariable Long albumId,
                                     @ApiIgnore @PathVariable Long imageId) {
        logger.info("DELETE IMAGE");
        ResponseModel responseModel = new ResponseModel();

        Album album = entityService.findOneByAttrWithOptions(Album.class, Album_.ID, String.valueOf(albumId), Album_.USER, Album_.IMAGES);

        if (album == null || !(album.isHost(principal))) {
            return new ResponseModel(HttpStatus.NOT_FOUND);
        }

        Stream<Image> imagesStream = (album.getImages().size() > 100)
                ? album.getImages().parallelStream()
                : album.getImages().stream();

        //find image
        Optional<Image> optionalImageToDel = imagesStream.filter((img) -> img.getId().equals(imageId)).findFirst();
        optionalImageToDel.ifPresentOrElse(
                (image) -> {
                        //remove image from list and save album
                        album.getImages().remove(image);
                        albumService.save(album);

                        //delete image file
                        multipartFileImageFactory.create(image.getSrc()).deleteImages();

                        responseModel.addAttribute(ResponseModel.ATTR_IMAGES, album.getImages());
                },
                //no image found, send 500
                () -> responseModel.setStatus(HttpStatus.NOT_FOUND)
        );

        return responseModel;
    }

    @ApiOperation(value = "increment image's \"view_count\" attribute")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "page"),
            @ApiImplicitParam(name = "offset"),
            @ApiImplicitParam(name = "limit"),
    })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "view_count incremented")
    })
    @PostMapping("/{id}/increment-vc")
    public void count(@PathVariable Long id) {
        Optional<Image> image = imageService.findById(id);
        if (image.isPresent()) {
            Integer viewCount = image.get().getViewCount();
            image.get().setViewCount(++viewCount);
            imageService.save(image.get());
        }
    }

}
<file_sep>/src/main/java/com/gallery/model/dto/PaginationData.java
package com.gallery.model.dto;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

//@ApiModel(value = "Pagination DTO", description = "DTO to hold data about page, offset and limit GET parameters")
@Setter
@Getter
@ToString
public class PaginationData {

    private int offset;

    private int page;

    //all items total found
    private long itemsTotal;

    //items total for one page
    private int pageItemsTotal;

    @Setter(AccessLevel.NONE)
    private int pageTotal;

    public PaginationData(int offset, int page, long itemsTotal, int pageItemsTotal, int itemsSizePerPage) {
        this.offset = offset;
        //to prev page if no items found AND page is not first
        if(pageItemsTotal == 0 && page > 0){
            page--;
        }
        this.page = page;
        this.itemsTotal = itemsTotal;
        this.pageItemsTotal = pageItemsTotal;
        int pageTotalVal = 1;
        if( itemsTotal > itemsSizePerPage){
            try {
                pageTotalVal = (int) Math.floor((itemsTotal / (double) itemsSizePerPage)) + 1;
                if(itemsTotal % itemsSizePerPage == 0){
                    pageTotalVal--;
                }
            }
            catch (ArithmeticException e){
                e.printStackTrace();
            }
        }
        this.pageTotal = pageTotalVal;
    }

}<file_sep>/src/main/java/com/gallery/model/entity/user/User_.java
package com.gallery.model.entity.user;

import com.gallery.model.entity.AbstractEntity_;
import com.gallery.model.entity.album.Album;
import javax.annotation.Generated;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.SetAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(User.class)
public class User_ extends AbstractEntity_ {

	public static volatile SingularAttribute<User, String> emailToken;
	public static volatile ListAttribute<User, Album> albums;
	public static volatile SingularAttribute<User, String> avaPath;
	public static volatile SetAttribute<User, User> subscribers;
	public static volatile SingularAttribute<User, String> authToken;
	public static volatile SetAttribute<User, Role> roles;
	public static volatile SingularAttribute<User, Boolean> enabled;
	public static volatile SingularAttribute<User, String> password;
	public static volatile SingularAttribute<User, String> passwordToken;
	public static volatile SingularAttribute<User, Boolean> accountExpired;
	public static volatile SingularAttribute<User, Boolean> locked;
	public static volatile SingularAttribute<User, Boolean> credentialsExpired;
	public static volatile SingularAttribute<User, String> email;
	public static volatile SingularAttribute<User, String> username;

	public static final String EMAIL_TOKEN = "emailToken";
	public static final String ALBUMS = "albums";
	public static final String AVA_PATH = "avaPath";
	public static final String SUBSCRIBERS = "subscribers";
	public static final String AUTH_TOKEN = "authToken";
	public static final String ROLES = "roles";
	public static final String ENABLED = "enabled";
	public static final String PASSWORD = "<PASSWORD>";
	public static final String PASSWORD_TOKEN = "<PASSWORD>";
	public static final String ACCOUNT_EXPIRED = "accountExpired";
	public static final String LOCKED = "locked";
	public static final String CREDENTIALS_EXPIRED = "credentialsExpired";
	public static final String EMAIL = "email";
	public static final String USERNAME = "username";

}<file_sep>/src/main/java/com/gallery/service/delayedtask/DelayedEntityRemover.java
package com.gallery.service.delayedtask;

import com.gallery.model.entity.AbstractEntity;
import java.util.Queue;

/**
 * Remove entity with delay. (see {@link TaskItem})
 * @param <T> type of entity
 */
public interface DelayedEntityRemover<T extends AbstractEntity> {

    Queue<TaskItem<T>> getItems();

    void addEntity(T entity);

    //remove from list
    boolean removeEntity(T entity);

    //delete
    //void deleteEntity(T item);
}
<file_sep>/src/test/java/com/gallery/service/ImageServiceTest.java
package com.gallery.service;

import com.gallery.model.entity.album.Album;
import com.gallery.model.entity.album.Image;
import com.gallery.service.common.ServiceTest;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Optional;

public class ImageServiceTest implements ServiceTest {

    @Autowired
    private ImageService imageService;
    @Autowired
    private AlbumService albumService;

    @Test
    public void save(){
        long albumId = 24;

        Optional<Album> optionalAlbum = albumService.findByIdWithImages(albumId);
        Assert.assertTrue(optionalAlbum.isPresent());

        int countBefore = optionalAlbum.get().getImages().size();

        Image image = new Image("image#111");
        optionalAlbum.get().getImages().add(image);
        albumService.save(optionalAlbum.get());

        optionalAlbum = albumService.findByIdWithImages(albumId);
        long countAfter = optionalAlbum.get().getImages().size();
        Assert.assertEquals(countBefore + 1, countAfter);
    }

    @Test
    public void delete() {
        long albumId = 24;

        Optional<Album> optionalAlbum = albumService.findByIdWithImages(albumId);
        Assert.assertTrue(optionalAlbum.isPresent());

        int countBefore = optionalAlbum.get().getImages().size();
        Assert.assertTrue(countBefore > 0);

        Image image = optionalAlbum.get().getImages().get(0);
        optionalAlbum.get().getImages().remove(image);

        albumService.save(optionalAlbum.get());

        optionalAlbum = albumService.findByIdWithImages(albumId);
        long countAfter = optionalAlbum.get().getImages().size();
        Assert.assertEquals(countBefore - 1, countAfter);
    }
}
<file_sep>/src/test/java/com/gallery/web/client/AlbumControllerTest.java
package com.gallery.web.client;

import com.gallery.FunctionUtilTest;
import com.gallery.model.entity.album.Album;
import com.gallery.model.entity.user.User;
import com.gallery.service.AlbumService;
import com.gallery.service.UserService;
import com.gallery.util.ResponseModel;
import com.gallery.web.client.common.ControllerClientTest;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.ExpectedCount;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.List;

public class AlbumControllerTest extends ControllerClientTest {

    @Autowired
    private AlbumService albumService;

    @Autowired
    private UserService userService;

    @Test
    public void findAll() throws Exception {
        List<Album> albumsFound = (List<Album>) albumService.findAll();
        int page = 4;
        int foundSize = albumsFound.size();
        ResponseModel responseModelExpected = new ResponseModel("albums", albumsFound);
//        String expectedJsonContent = asJson(responseModelExpected);

        RestTemplate restTemplate = new RestTemplate();
        server = MockRestServiceServer.bindTo(restTemplate).build();

        UriComponents uriComponents = UriComponentsBuilder.fromUri(URI.create(albumAllUri))
                .queryParam("offset", page)
                .queryParam("limit", 10000)
                .build();
        server.expect(ExpectedCount.once(), requestTo(uriComponents.toUriString()))
                .andExpect(method(HttpMethod.GET))
                .andRespond(withStatusOkAndJsonBody(responseModelExpected));

        ResponseModel responseModelResponse = restTemplate.getForObject(uriComponents.toUriString(), ResponseModel.class);
        server.verify();
        int albumsSize = FunctionUtilTest.getResponseModelCollectionAttrSize(responseModelResponse, ResponseModel.ATTR_ALBUMS);
        Assert.assertEquals(HttpStatus.OK.value(), responseModelResponse.getStatusCode());
        Assert.assertEquals(foundSize, albumsSize);

    }

    @Test
    public void findAllByUser() throws Exception {
        String username = "Dominick";
        User user = userService.findByUsernameWithAlbums(username);
        Assert.assertNotNull(user);

        int albumsFoundSize = user.getAlbums().size();
        RestTemplate restTemplate = new RestTemplate();
        server = MockRestServiceServer.bindTo(restTemplate).build();

        String url = albumByUserUriWithUsername.replace("{username}", username);

        server.expect(expectOnce(), requestTo(url))
                .andExpect(method(HttpMethod.GET))
                .andRespond(withStatusOkAndJsonBody(new ResponseModel("albums", user.getAlbums())));

        ResponseModel responseModel = restTemplate.getForObject(url, ResponseModel.class);

        int size = FunctionUtilTest.getResponseModelCollectionAttrSize(responseModel, ResponseModel.ATTR_ALBUMS);
        Assert.assertEquals(albumsFoundSize, size);
    }

}
<file_sep>/src/main/java/com/gallery/model/entity/AbstractEntity_.java
package com.gallery.model.entity;

import java.util.Date;
import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(AbstractEntity.class)
public class AbstractEntity_ {

	public static volatile SingularAttribute<AbstractEntity, Date> createdAt;
	public static volatile SingularAttribute<AbstractEntity, Long> id;

	public static final String CREATED_AT = "createdAt";
	public static final String ID = "id";

}

