diff --git a/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java b/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java index 88352473fbc229f1be55176f9b261054475ef74a..92f09e6af64bb9a487734d7d8b59cd457a11dbc5 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java +++ b/src/main/java/de/deadlocker8/budgetmaster/accounts/Account.java @@ -1,6 +1,7 @@ package de.deadlocker8.budgetmaster.accounts; import com.google.gson.annotations.Expose; +import de.deadlocker8.budgetmaster.images.Image; import de.deadlocker8.budgetmaster.transactions.Transaction; import javax.persistence.*; @@ -30,20 +31,21 @@ public class Account private Boolean isDefault = false; private Boolean isReadOnly = false; + @ManyToOne @Expose - private String iconPath; + private Image icon; @Expose private AccountType type; - public Account(String name, AccountType type, String iconPath) + public Account(String name, AccountType type, Image icon) { this.name = name; this.type = type; this.isSelected = false; this.isDefault = false; this.isReadOnly = false; - this.iconPath = iconPath; + this.icon = icon; } public Account(String name, AccountType type) @@ -125,14 +127,14 @@ public class Account this.type = type; } - public String getIconPath() + public Image getIcon() { - return iconPath; + return icon; } - public void setIconPath(String iconPath) + public void setIcon(Image icon) { - this.iconPath = iconPath; + this.icon = icon; } @Override @@ -146,7 +148,7 @@ public class Account ", isDefault=" + isDefault + ", isReadOnly=" + isReadOnly + ", type=" + type + - ", iconPath=" + iconPath + + ", icon=" + icon + '}'; } @@ -162,12 +164,12 @@ public class Account Objects.equals(ID, account.ID) && Objects.equals(name, account.name) && type == account.type && - Objects.equals(iconPath, account.iconPath); + Objects.equals(icon, account.icon); } @Override public int hashCode() { - return Objects.hash(ID, name, isSelected, isDefault, isReadOnly, type, iconPath); + return Objects.hash(ID, name, isSelected, isDefault, isReadOnly, type, icon); } } \ No newline at end of file diff --git a/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java index 5651fd483c37b521bdedc62b2595b7ce42f9518e..38b3e8dd1fdcdfc34847f8b14183dccd2f86ec07 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java +++ b/src/main/java/de/deadlocker8/budgetmaster/accounts/AccountController.java @@ -1,6 +1,7 @@ package de.deadlocker8.budgetmaster.accounts; import de.deadlocker8.budgetmaster.controller.BaseController; +import de.deadlocker8.budgetmaster.images.ImageService; import de.deadlocker8.budgetmaster.settings.SettingsService; import de.deadlocker8.budgetmaster.utils.Mappings; import de.deadlocker8.budgetmaster.utils.ResourceNotFoundException; @@ -27,12 +28,14 @@ public class AccountController extends BaseController { private final AccountService accountService; private final SettingsService settingsService; + private final ImageService imageService; @Autowired - public AccountController(AccountService accountService, SettingsService settingsService) + public AccountController(AccountService accountService, SettingsService settingsService, ImageService imageService) { this.accountService = accountService; this.settingsService = settingsService; + this.imageService = imageService; } @GetMapping(value = "/{ID}/select") @@ -129,7 +132,7 @@ public class AccountController extends BaseController Account emptyAccount = new Account(); model.addAttribute("account", emptyAccount); model.addAttribute("settings", settingsService.getSettings()); - model.addAttribute("iconImages", List.of("https://localhost:9000/touch_icon.png", "https://localhost:9000/touch_icon.png")); + model.addAttribute("availableImages", imageService.getRepository().findAll()); return "accounts/newAccount"; } @@ -144,7 +147,7 @@ public class AccountController extends BaseController model.addAttribute("account", accountOptional.get()); model.addAttribute("settings", settingsService.getSettings()); - model.addAttribute("iconImages", List.of("https://localhost:9000/touch_icon.png", "https://localhost:9000/touch_icon.png")); + model.addAttribute("availableImages", imageService.getRepository().findAll()); return "accounts/newAccount"; } @@ -168,7 +171,7 @@ public class AccountController extends BaseController model.addAttribute("error", bindingResult); model.addAttribute("account", account); model.addAttribute("settings", settingsService.getSettings()); - model.addAttribute("iconImages", List.of("https://localhost:9000/touch_icon.png", "https://localhost:9000/touch_icon.png")); + model.addAttribute("availableImages", imageService.getRepository().findAll()); return "accounts/newAccount"; } else @@ -187,7 +190,7 @@ public class AccountController extends BaseController { Account existingAccount = existingAccountOptional.get(); existingAccount.setName(account.getName()); - existingAccount.setIconPath(account.getIconPath()); + existingAccount.setIcon(account.getIcon()); accountService.getRepository().save(existingAccount); } } diff --git a/src/main/java/de/deadlocker8/budgetmaster/images/Image.java b/src/main/java/de/deadlocker8/budgetmaster/images/Image.java new file mode 100644 index 0000000000000000000000000000000000000000..973a25107a0d3d5047a905a020f41bc5a3211f56 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/images/Image.java @@ -0,0 +1,85 @@ +package de.deadlocker8.budgetmaster.images; + +import com.google.gson.annotations.Expose; +import de.deadlocker8.budgetmaster.accounts.Account; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.List; +import java.util.Objects; + +@Entity +public class Image +{ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Expose + private Integer ID; + + @NotNull + @Size(min = 1) + @Expose + private String imagePath; + + @OneToMany(mappedBy = "icon", fetch = FetchType.LAZY) + private List<Account> referringAccounts; + + public Image(String imagePath) + { + this.imagePath = imagePath; + } + + public Image() + { + } + + public Integer getID() + { + return ID; + } + + public void setID(Integer ID) + { + this.ID = ID; + } + + public String getImagePath() + { + return imagePath; + } + + public void setImagePath(String imagePath) + { + this.imagePath = imagePath; + } + + public List<Account> getReferringAccounts() + { + return referringAccounts; + } + + @Override + public String toString() + { + return "Image{" + + "ID=" + ID + + ", imagePath='" + imagePath + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) + { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + Image image = (Image) o; + return Objects.equals(ID, image.ID) && Objects.equals(imagePath, image.imagePath); + } + + @Override + public int hashCode() + { + return Objects.hash(ID, imagePath); + } +} \ No newline at end of file diff --git a/src/main/java/de/deadlocker8/budgetmaster/images/ImageRepository.java b/src/main/java/de/deadlocker8/budgetmaster/images/ImageRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..db770d7076ea3daa70024fb4517f5cfcd3b185b4 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/images/ImageRepository.java @@ -0,0 +1,8 @@ +package de.deadlocker8.budgetmaster.images; + +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface ImageRepository extends JpaRepository<Image, Integer> +{ +} \ No newline at end of file diff --git a/src/main/java/de/deadlocker8/budgetmaster/images/ImageService.java b/src/main/java/de/deadlocker8/budgetmaster/images/ImageService.java new file mode 100644 index 0000000000000000000000000000000000000000..cd364e3e69e49b338ef76e51da5da0eb15d382c6 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/images/ImageService.java @@ -0,0 +1,51 @@ +package de.deadlocker8.budgetmaster.images; + +import de.deadlocker8.budgetmaster.services.Resetable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ImageService implements Resetable +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ImageService.class); + + private final ImageRepository imageRepository; + + @Autowired + public ImageService(ImageRepository imageRepository) + { + this.imageRepository = imageRepository; + } + + public ImageRepository getRepository() + { + return imageRepository; + } + + @Override + public void deleteAll() + { + // TODO: set placeholder image for all accounts + imageRepository.deleteAll(); + } + + @Override + public void createDefaults() + { + if(imageRepository.findAll().isEmpty()) + { + // TODO: insert placeholder image +// Account placeholder = new Account("Placeholder", AccountType.ALL); +// accountRepository.save(placeholder); +// LOGGER.debug("Created placeholder account"); +// +// Account account = accountRepository.save(new Account(Localization.getString(Strings.ACCOUNT_DEFAULT_NAME), AccountType.CUSTOM)); +// selectAccount(account.getID()); +// setAsDefaultAccount(account.getID()); +// LOGGER.debug("Created default account"); + } + + } +} diff --git a/src/main/resources/templates/accounts/accountFunctions.ftl b/src/main/resources/templates/accounts/accountFunctions.ftl index 407c3b3534c7dadbde758bd4e05b58a31044a131..ae5b1c0545ffd076fe6819c7c26078cb9f60e0db 100644 --- a/src/main/resources/templates/accounts/accountFunctions.ftl +++ b/src/main/resources/templates/accounts/accountFunctions.ftl @@ -4,7 +4,7 @@ <div id="modalAccountIconSelect" class="modal modal-fixed-footer background-color"> <div class="modal-content"> <div class="row"> - <#list iconImages as image> + <#list availableImages as image> <@accountIconOption image/> </#list> </div> @@ -16,10 +16,10 @@ </div> </#macro> -<#macro accountIconOption icon> +<#macro accountIconOption image> <div class="col s4 m2 l2 account-icon-option-column"> <div class="account-icon-option"> - <img src="${icon}" class="account-icon-preview" alt="${icon}" data-image-id="1"/> + <img src="${image.getImagePath()}" class="account-icon-preview" alt="${image.getImagePath()}" data-image-id="${image.getID()}"/> </div> </div> </#macro> \ No newline at end of file diff --git a/src/main/resources/templates/accounts/accounts.ftl b/src/main/resources/templates/accounts/accounts.ftl index 55b60dd8ef244338350741c4464ade43f088a7da..f8256aeff4403135e6e09359eba23fcbf532a107 100644 --- a/src/main/resources/templates/accounts/accounts.ftl +++ b/src/main/resources/templates/accounts/accounts.ftl @@ -41,7 +41,7 @@ <a href="<@s.url '/accounts/${account.getID()?c}/toggleReadOnly'/>" class="btn-flat no-padding text-default tooltipped" data-position="right" data-tooltip="${toolTipText}">${lockIcon}</a> </#if> </td> - <td><#if account.getIconPath()?has_content><img src="${account.getIconPath()}" class="account-icon"/></#if></td> + <td><#if account.getIcon()??><img src="${account.getIcon().getImagePath()}" class="account-icon"/></#if></td> <td>${account.getName()}</td> <td> <a href="<@s.url '/accounts/${account.getID()?c}/edit'/>" class="btn-flat no-padding text-default"><i class="material-icons left">edit</i></a> diff --git a/src/main/resources/templates/accounts/newAccount.ftl b/src/main/resources/templates/accounts/newAccount.ftl index d27d2205db4692c4ffe3d791592993e23a2af6e5..83b2aa607d60c3737a93894cd4816b945471fc27 100644 --- a/src/main/resources/templates/accounts/newAccount.ftl +++ b/src/main/resources/templates/accounts/newAccount.ftl @@ -54,11 +54,12 @@ <div id="account-icon" class="valign-wrapper"> <a href="#modalAccountIconSelect" id="account-icon-preview" class="modal-trigger"> - <img id="account-icon-preview-icon" src="<#if account.getIconPath()?has_content>${account.getIconPath()}</#if>" class="account-icon-preview <#if account.getIconPath()?has_content == false>hidden</#if>"/> - <div id="account-icon-placeholder" class="<#if account.getIconPath()?has_content>hidden</#if>">${locale.getString("account.new.icon.placeholder")}</div> + <img id="account-icon-preview-icon" src="<#if account.getIcon()??>${account.getIcon().getImagePath()}</#if>" class="account-icon-preview <#if account.getIcon()?? == false>hidden</#if>"/> + <div id="account-icon-placeholder" class="<#if account.getIcon()??>hidden</#if>">${locale.getString("account.new.icon.placeholder")}</div> </a> <@header.buttonFlat url='' icon='delete' id='button-remove-account-icon' localizationKey='' classes="no-padding text-default" noUrl=true/> - <input id="hidden-input-account-icon" type="hidden" name="iconPath" value="<#if account.getIconPath()??>${account.getIconPath()}</#if>"> + + <input id="hidden-input-account-icon" type="hidden" name="icon" value="<#if account.getIcon()??>${account.getIcon().getID()?c}</#if>"> </div> </div> </div> diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/DatabaseParser_v5Test.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/DatabaseParser_v5Test.java index e73826c26dc2adfbacc3e7709e91048a7a9f30c8..d46effdbbb90ffc0bab8ae5d5c8b1ae01df26542 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/unit/database/DatabaseParser_v5Test.java +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/DatabaseParser_v5Test.java @@ -8,6 +8,7 @@ import de.deadlocker8.budgetmaster.charts.Chart; import de.deadlocker8.budgetmaster.charts.ChartType; import de.deadlocker8.budgetmaster.database.Database; import de.deadlocker8.budgetmaster.database.DatabaseParser_v5; +import de.deadlocker8.budgetmaster.images.Image; import de.thecodelabs.utils.util.Localization; import de.thecodelabs.utils.util.Localization.LocalizationDelegate; import org.junit.Before; @@ -95,7 +96,8 @@ public class DatabaseParser_v5Test DatabaseParser_v5 importer = new DatabaseParser_v5(json); Database database = importer.parseDatabaseFromJSON(); - final Account account = new Account("Second Account", AccountType.CUSTOM, "myPath/to/fancy/icon.png"); + // TODO + final Account account = new Account("Second Account", AccountType.CUSTOM, new Image()); account.setID(3); assertThat(database.getAccounts()).hasSize(3)