diff --git a/docs/ER-DiagramFile.mwb b/docs/ER-DiagramFile.mwb
deleted file mode 100644
index c05526f1..00000000
Binary files a/docs/ER-DiagramFile.mwb and /dev/null differ
diff --git a/docs/ER-DiagramFile.mwb.bak b/docs/ER-DiagramFile.mwb.bak
deleted file mode 100644
index 24c783ba..00000000
Binary files a/docs/ER-DiagramFile.mwb.bak and /dev/null differ
diff --git a/docs/ER-Diagram v1.png b/docs/SqlDatabase/ER-Diagram v1.png
similarity index 100%
rename from docs/ER-Diagram v1.png
rename to docs/SqlDatabase/ER-Diagram v1.png
diff --git a/docs/ER-Diagram v2.png b/docs/SqlDatabase/ER-Diagram v2.png
similarity index 100%
rename from docs/ER-Diagram v2.png
rename to docs/SqlDatabase/ER-Diagram v2.png
diff --git a/docs/SqlDatabase/ER-Diagram v3.png b/docs/SqlDatabase/ER-Diagram v3.png
new file mode 100644
index 00000000..af63917c
Binary files /dev/null and b/docs/SqlDatabase/ER-Diagram v3.png differ
diff --git a/docs/SqlDatabase/ER-Diagram v4.png b/docs/SqlDatabase/ER-Diagram v4.png
new file mode 100644
index 00000000..bffbaf8a
Binary files /dev/null and b/docs/SqlDatabase/ER-Diagram v4.png differ
diff --git a/docs/SqlDatabase/ER-Diagram v5.png b/docs/SqlDatabase/ER-Diagram v5.png
new file mode 100644
index 00000000..df229943
Binary files /dev/null and b/docs/SqlDatabase/ER-Diagram v5.png differ
diff --git a/docs/SqlDatabase/ER-DiagramFile.mwb b/docs/SqlDatabase/ER-DiagramFile.mwb
new file mode 100644
index 00000000..15edab11
Binary files /dev/null and b/docs/SqlDatabase/ER-DiagramFile.mwb differ
diff --git a/docs/SqlDatabase/ER-DiagramFile.mwb.bak b/docs/SqlDatabase/ER-DiagramFile.mwb.bak
new file mode 100644
index 00000000..c348da33
Binary files /dev/null and b/docs/SqlDatabase/ER-DiagramFile.mwb.bak differ
diff --git a/docs/SqlDatabase/Sql_script_v1.1.sql b/docs/SqlDatabase/Sql_script_v1.1.sql
new file mode 100644
index 00000000..63104556
--- /dev/null
+++ b/docs/SqlDatabase/Sql_script_v1.1.sql
@@ -0,0 +1,203 @@
+-- MySQL Workbench Forward Engineering
+
+SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
+SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
+SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
+
+-- -----------------------------------------------------
+-- Schema apbaluna
+-- -----------------------------------------------------
+
+-- -----------------------------------------------------
+-- Schema apbaluna
+-- -----------------------------------------------------
+CREATE SCHEMA IF NOT EXISTS `apbaluna` DEFAULT CHARACTER SET utf8 ;
+USE `apbaluna` ;
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Charities`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Charities` (
+ `UUID_charities` CHAR(36) NOT NULL,
+ `org_number` VARCHAR(255) NOT NULL,
+ `charity_name` VARCHAR(255) NOT NULL,
+ `charity_description` VARCHAR(255) NOT NULL,
+ `charity_link` VARCHAR(255) NOT NULL,
+ `pre_approved` TINYINT NOT NULL,
+ `status` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`UUID_charities`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`User`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`User` (
+ `UUID_User` CHAR(36) NOT NULL,
+ `user_name` VARCHAR(255) NOT NULL,
+ `user_email` VARCHAR(255) NOT NULL,
+ `role` VARCHAR(45) NOT NULL,
+ PRIMARY KEY (`UUID_User`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Donations`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Donations` (
+ `UUID_Donations` CHAR(36) NOT NULL,
+ `amount` DECIMAL NOT NULL,
+ `date` DATE NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_Donations`),
+ INDEX `fk_Donations_Charities_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Donations_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Donations_Charities`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Donations_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Settings`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Settings` (
+ `User_UUID_User` CHAR(36) NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `language` VARCHAR(45) NOT NULL,
+ `lightmode` TINYINT NOT NULL,
+ PRIMARY KEY (`User_UUID_User`),
+ INDEX `fk_Settings_User1_idx` (`User_UUID_User` ASC) VISIBLE,
+ CONSTRAINT `fk_Settings_User1`
+ FOREIGN KEY (`User_UUID_User`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Messages`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Messages` (
+ `UUID_message` CHAR(36) NOT NULL,
+ `message_title` VARCHAR(255) NOT NULL,
+ `message_content` VARCHAR(255) NOT NULL,
+ `message_date` DATE NOT NULL,
+ `sender_user_id` CHAR(36) NULL,
+ `sender_charity_id` CHAR(36) NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_message`),
+ INDEX `fk_Messages_User1_idx` (`sender_user_id` ASC) VISIBLE,
+ INDEX `fk_Messages_Charities1_idx` (`sender_charity_id` ASC) VISIBLE,
+ INDEX `fk_Messages_User2_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Messages_User1`
+ FOREIGN KEY (`sender_user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_Charities1`
+ FOREIGN KEY (`sender_charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_User2`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Feedback`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Feedback` (
+ `UUID_feedback` CHAR(36) NOT NULL,
+ `feedback_comment` VARCHAR(255) NOT NULL,
+ `feedback_date` DATE NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_feedback`),
+ INDEX `fk_Feedback_Charities1_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Feedback_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Feedback_Charities1`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Feedback_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Categories`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Categories` (
+ `category_id` INT NOT NULL AUTO_INCREMENT,
+ `category` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`category_id`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Charity_Categories`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Charity_Categories` (
+ `Categories_category_id` INT NOT NULL,
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Categories_category_id`, `Charities_UUID_charities`),
+ INDEX `fk_Categories_has_Charities_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ INDEX `fk_Categories_has_Charities_Categories1_idx` (`Categories_category_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Categories_has_Charities_Categories1`
+ FOREIGN KEY (`Categories_category_id`)
+ REFERENCES `apbaluna`.`Categories` (`category_id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Categories_has_Charities_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`CharityUsers`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`CharityUsers` (
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ `User_UUID_User` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Charities_UUID_charities`, `User_UUID_User`),
+ INDEX `fk_Charities_has_User_User1_idx` (`User_UUID_User` ASC) VISIBLE,
+ INDEX `fk_Charities_has_User_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ CONSTRAINT `fk_Charities_has_User_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Charities_has_User_User1`
+ FOREIGN KEY (`User_UUID_User`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+USE `apbaluna`;
+
+SET SQL_MODE=@OLD_SQL_MODE;
+SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
+SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
diff --git a/docs/SqlDatabase/Sql_script_v1.2.sql b/docs/SqlDatabase/Sql_script_v1.2.sql
new file mode 100644
index 00000000..4acb7589
--- /dev/null
+++ b/docs/SqlDatabase/Sql_script_v1.2.sql
@@ -0,0 +1,203 @@
+-- MySQL Workbench Forward Engineering
+
+SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
+SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
+SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
+
+-- -----------------------------------------------------
+-- Schema apbaluna
+-- -----------------------------------------------------
+
+-- -----------------------------------------------------
+-- Schema apbaluna
+-- -----------------------------------------------------
+CREATE SCHEMA IF NOT EXISTS `apbaluna` DEFAULT CHARACTER SET utf8 ;
+USE `apbaluna` ;
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Charities`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Charities` (
+ `UUID_charities` CHAR(36) NOT NULL,
+ `org_number` VARCHAR(255) NOT NULL,
+ `charity_name` VARCHAR(255) NOT NULL,
+ `charity_description` VARCHAR(255) NOT NULL,
+ `charity_link` VARCHAR(255) NOT NULL,
+ `pre_approved` TINYINT NOT NULL,
+ `status` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`UUID_charities`),
+ UNIQUE INDEX `org_number_UNIQUE` (`org_number` ASC) VISIBLE)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`User`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`User` (
+ `UUID_User` CHAR(36) NOT NULL,
+ `user_name` VARCHAR(255) NOT NULL,
+ `user_email` VARCHAR(255) NOT NULL,
+ `role` VARCHAR(45) NOT NULL,
+ PRIMARY KEY (`UUID_User`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Donations`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Donations` (
+ `UUID_Donations` CHAR(36) NOT NULL,
+ `amount` DECIMAL NOT NULL,
+ `date` DATE NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_Donations`),
+ INDEX `fk_Donations_Charities_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Donations_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Donations_Charities`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Donations_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Settings`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Settings` (
+ `User_UUID_User` CHAR(36) NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `language` VARCHAR(45) NOT NULL,
+ `lightmode` TINYINT NOT NULL,
+ PRIMARY KEY (`User_UUID_User`),
+ INDEX `fk_Settings_User1_idx` (`User_UUID_User` ASC) VISIBLE,
+ CONSTRAINT `fk_Settings_User1`
+ FOREIGN KEY (`User_UUID_User`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Messages`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Messages` (
+ `UUID_message` CHAR(36) NOT NULL,
+ `message_title` VARCHAR(255) NOT NULL,
+ `message_content` VARCHAR(255) NOT NULL,
+ `message_date` DATE NOT NULL,
+ `sender_user_id` CHAR(36) NULL,
+ `sender_charity_id` CHAR(36) NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_message`),
+ INDEX `fk_Messages_User1_idx` (`sender_user_id` ASC) VISIBLE,
+ INDEX `fk_Messages_Charities1_idx` (`sender_charity_id` ASC) VISIBLE,
+ INDEX `fk_Messages_User2_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Messages_User1`
+ FOREIGN KEY (`sender_user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_Charities1`
+ FOREIGN KEY (`sender_charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_User2`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Feedback`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Feedback` (
+ `UUID_feedback` CHAR(36) NOT NULL,
+ `feedback_comment` VARCHAR(255) NOT NULL,
+ `feedback_date` DATE NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_feedback`),
+ INDEX `fk_Feedback_Charities1_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Feedback_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Feedback_Charities1`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Feedback_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Categories`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Categories` (
+ `category_id` INT NOT NULL AUTO_INCREMENT,
+ `category` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`category_id`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Charity_Categories`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Charity_Categories` (
+ `Categories_category_id` INT NOT NULL,
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Categories_category_id`, `Charities_UUID_charities`),
+ INDEX `fk_Categories_has_Charities_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ INDEX `fk_Categories_has_Charities_Categories1_idx` (`Categories_category_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Categories_has_Charities_Categories1`
+ FOREIGN KEY (`Categories_category_id`)
+ REFERENCES `apbaluna`.`Categories` (`category_id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Categories_has_Charities_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`CharityUsers`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`CharityUsers` (
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ `User_UUID_User` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Charities_UUID_charities`, `User_UUID_User`),
+ INDEX `fk_Charities_has_User_User1_idx` (`User_UUID_User` ASC) VISIBLE,
+ INDEX `fk_Charities_has_User_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ CONSTRAINT `fk_Charities_has_User_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Charities_has_User_User1`
+ FOREIGN KEY (`User_UUID_User`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+SET SQL_MODE=@OLD_SQL_MODE;
+SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
+SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
diff --git a/docs/SqlDatabase/Sql_scriptv1.0.sql b/docs/SqlDatabase/Sql_scriptv1.0.sql
new file mode 100644
index 00000000..529aac8f
--- /dev/null
+++ b/docs/SqlDatabase/Sql_scriptv1.0.sql
@@ -0,0 +1,213 @@
+-- MySQL Workbench Forward Engineering
+
+SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
+SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
+SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
+
+-- -----------------------------------------------------
+-- Schema apbaluna
+-- -----------------------------------------------------
+
+-- -----------------------------------------------------
+-- Schema apbaluna
+-- -----------------------------------------------------
+CREATE SCHEMA IF NOT EXISTS `apbaluna` DEFAULT CHARACTER SET utf8 ;
+USE `apbaluna` ;
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Charities`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Charities` (
+ `UUID_charities` CHAR(36) NOT NULL,
+ `org_number` VARCHAR(255) NOT NULL,
+ `charity_name` VARCHAR(255) NOT NULL,
+ `charity_description` VARCHAR(255) NOT NULL,
+ `charity_link` VARCHAR(255) NOT NULL,
+ `pre_approved` TINYINT NOT NULL,
+ `status` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`UUID_charities`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`User`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`User` (
+ `UUID_User` CHAR(36) NOT NULL,
+ `user_name` VARCHAR(255) NOT NULL,
+ `user_email` VARCHAR(255) NOT NULL,
+ `role` VARCHAR(45) NOT NULL,
+ PRIMARY KEY (`UUID_User`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Donations`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Donations` (
+ `UUID_Donations` CHAR(36) NOT NULL,
+ `amount` DECIMAL NOT NULL,
+ `date` DATE NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_Donations`),
+ INDEX `fk_Donations_Charities_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Donations_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Donations_Charities`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Donations_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Settings`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Settings` (
+ `User_UUID_User` CHAR(36) NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `language` VARCHAR(45) NOT NULL,
+ `lightmode` TINYINT NOT NULL,
+ PRIMARY KEY (`User_UUID_User`),
+ INDEX `fk_Settings_User1_idx` (`User_UUID_User` ASC) VISIBLE,
+ CONSTRAINT `fk_Settings_User1`
+ FOREIGN KEY (`User_UUID_User`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Messages`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Messages` (
+ `UUID_message` CHAR(36) NOT NULL,
+ `message_title` VARCHAR(255) NOT NULL,
+ `message_content` VARCHAR(255) NOT NULL,
+ `message_date` DATE NOT NULL,
+ `sender_user_id` CHAR(36) NULL,
+ `sender_charity_id` CHAR(36) NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_message`),
+ INDEX `fk_Messages_User1_idx` (`sender_user_id` ASC) VISIBLE,
+ INDEX `fk_Messages_Charities1_idx` (`sender_charity_id` ASC) VISIBLE,
+ INDEX `fk_Messages_User2_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Messages_User1`
+ FOREIGN KEY (`sender_user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_Charities1`
+ FOREIGN KEY (`sender_charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_User2`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Feedback`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Feedback` (
+ `UUID_feedback` CHAR(36) NOT NULL,
+ `feedback_comment` VARCHAR(255) NOT NULL,
+ `feedback_date` DATE NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_feedback`),
+ INDEX `fk_Feedback_Charities1_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Feedback_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Feedback_Charities1`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Feedback_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Categories`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Categories` (
+ `category_id` INT NOT NULL AUTO_INCREMENT,
+ `category` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`category_id`))
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`Charity_Categories`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`Charity_Categories` (
+ `Categories_category_id` INT NOT NULL,
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Categories_category_id`, `Charities_UUID_charities`),
+ INDEX `fk_Categories_has_Charities_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ INDEX `fk_Categories_has_Charities_Categories1_idx` (`Categories_category_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Categories_has_Charities_Categories1`
+ FOREIGN KEY (`Categories_category_id`)
+ REFERENCES `apbaluna`.`Categories` (`category_id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Categories_has_Charities_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+
+-- -----------------------------------------------------
+-- Table `apbaluna`.`CharityUsers`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS `apbaluna`.`CharityUsers` (
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ `User_UUID_User` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Charities_UUID_charities`, `User_UUID_User`),
+ INDEX `fk_Charities_has_User_User1_idx` (`User_UUID_User` ASC) VISIBLE,
+ INDEX `fk_Charities_has_User_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ CONSTRAINT `fk_Charities_has_User_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Charities_has_User_User1`
+ FOREIGN KEY (`User_UUID_User`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ENGINE = InnoDB;
+
+USE `apbaluna`;
+
+DELIMITER $$
+USE `apbaluna`$$
+CREATE DEFINER = CURRENT_USER TRIGGER `apbaluna`.`User_AFTER_INSERT` AFTER INSERT ON `User` FOR EACH ROW
+BEGIN
+ INSERT INTO apbaluna.Settings (User_UUID_User, isAnonymous, language, lightmode) VALUES (NEW.UUID_User, 0, 'English', 0);
+END$$
+
+
+DELIMITER ;
+
+SET SQL_MODE=@OLD_SQL_MODE;
+SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
+SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
diff --git a/helpmehelpapplication/.ai/mcp/mcp.json b/helpmehelpapplication/.ai/mcp/mcp.json
new file mode 100644
index 00000000..e69de29b
diff --git a/helpmehelpapplication/.gitignore b/helpmehelpapplication/.gitignore
index e673575a..3fbc17a8 100644
--- a/helpmehelpapplication/.gitignore
+++ b/helpmehelpapplication/.gitignore
@@ -1,2 +1,4 @@
.idea/
-target/
\ No newline at end of file
+target/
+helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseConnection.java
+helpmehelpapplication/src/main/resources/fxml/test/
diff --git a/helpmehelpapplication/pom.xml b/helpmehelpapplication/pom.xml
index 1794a9e3..ec74b68b 100644
--- a/helpmehelpapplication/pom.xml
+++ b/helpmehelpapplication/pom.xml
@@ -33,7 +33,7 @@
org.seleniumhq.seleniumselenium-java
- 4.41.0
+ 4.43.0com.opencsv
@@ -62,6 +62,13 @@
2.4.240test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.23.0
+ test
+
@@ -111,6 +118,4 @@
-
-
\ No newline at end of file
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java
index 293aa2da..966cf897 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/HmHApplication.java
@@ -4,15 +4,18 @@
import java.net.http.HttpClient;
import java.util.Objects;
+
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
-import ntnu.systemutvikling.team6.database.DatabaseManager;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.database.DatabaseSetup;
import ntnu.systemutvikling.team6.models.Charity;
-import ntnu.systemutvikling.team6.models.CharityRegistry;
-import ntnu.systemutvikling.team6.scraper.APICharityScraper;
+import ntnu.systemutvikling.team6.models.registry.CharityRegistry;
+import ntnu.systemutvikling.team6.scraper.scraperComponents.APICharityScraper;
+import ntnu.systemutvikling.team6.service.APIToDatabaseService;
public class HmHApplication extends Application {
@Override
@@ -37,7 +40,8 @@ public void start(Stage stage) throws Exception {
public void init() {
/* Test and create tables to MySQL if ain't any */
try {
- DatabaseManager db = new DatabaseManager();
+ DatabaseConnection conn = new DatabaseConnection();
+ DatabaseSetup db = new DatabaseSetup(conn);
db.testConnection();
db.createTables();
} catch (Exception e) {
@@ -47,7 +51,9 @@ public void init() {
try {
HttpClient https = HttpClient.newHttpClient();
APICharityScraper scraper = new APICharityScraper(https);
- DatabaseManager db = new DatabaseManager();
+
+ DatabaseConnection conn = new DatabaseConnection();
+ APIToDatabaseService db = new APIToDatabaseService(conn);
if (scraper.checkConnection()) {
CharityRegistry charityRegistry = scraper.parseJSON(scraper.getJSONData());
@@ -59,6 +65,7 @@ public void init() {
} catch (Exception e) {
e.printStackTrace();
}
+ System.out.println("-- \n Init complete \n --");
}
public static void main(String[] args) {
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/AvailableOrganizationController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/AvailableOrganizationController.java
new file mode 100644
index 00000000..ee3a19f1
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/AvailableOrganizationController.java
@@ -0,0 +1,141 @@
+package ntnu.systemutvikling.team6.controller;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.FlowPane;
+import ntnu.systemutvikling.team6.controller.components.BaseController;
+import ntnu.systemutvikling.team6.controller.components.LoaderScene;
+import ntnu.systemutvikling.team6.controller.components.NavbarFooterController;
+import ntnu.systemutvikling.team6.controller.components.OrganizationCardController;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.database.Readers.CharitySelect;
+import ntnu.systemutvikling.team6.models.Charity;
+import ntnu.systemutvikling.team6.models.registry.CharityRegistry;
+
+/**
+ * This controller represents the available organization page, where the user can search for a
+ * charity and choose to donate to it. It also has a button to return to the front page. The user
+ * can search for a charity by typing in the search field, and the charities that match the search
+ * query will be displayed. The user can click on a charity to see more details about it, or click
+ * on the featured charity to see more details about it. The user can also switch to the charity
+ * page or donation page for the selected charity.
+ */
+public class AvailableOrganizationController extends BaseController implements NavbarFooterController {
+
+ @FXML private TextField searchField;
+ @FXML private FlowPane cardsContainer;
+
+ private Charity charity;
+ private List allCharities;
+
+ /**
+ * This method is used to initialize the available organization page. It retrieves all charities
+ * from the database and sets up a listener on the search field to filter the charities based on
+ * the user's input. It also clears the cards container to prepare for displaying the filtered
+ * charities. The method is called automatically when the page is loaded, and it sets up the
+ * initial state of the page.
+ */
+ @FXML
+ public void initialize() {
+ DatabaseConnection conn = new DatabaseConnection();
+ CharitySelect db = new CharitySelect(conn);
+ CharityRegistry charities = db.getCharitiesFromDB();
+ allCharities = charities.getAllCharities();
+
+ cardsContainer.getChildren().clear();
+
+ searchField
+ .textProperty()
+ .addListener(
+ (observable, oldValue, newValue) -> displayCharities(filterCharities(newValue)));
+ }
+
+ /**
+ * This method filters the charities based on the user's input in the search field.
+ *
+ * @param query is the user's input in the search field, which is used to filter the charities.
+ * @return a list of charities that match the search query.
+ */
+ private List filterCharities(String query) {
+ List matches = new ArrayList<>();
+
+ query = query.toLowerCase().trim();
+
+ if (query.isEmpty()) {
+ return matches;
+ }
+
+ for (Charity charity : allCharities) {
+ String name = charity.getName().toLowerCase();
+ String description = charity.getDescription().toLowerCase();
+
+ if (name.contains(query) || description.contains(query)) {
+ matches.add(charity);
+ }
+ }
+ return matches;
+ }
+
+ /**
+ * This method displays the charities in the cards container.
+ *
+ * @param charities is a list of charities to be displayed.
+ */
+ private void displayCharities(List charities) {
+ cardsContainer.getChildren().clear();
+
+ for (Charity charity : charities) {
+ try {
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/components/organizationCard.fxml"));
+ Parent card = loader.load();
+
+ OrganizationCardController cardController = loader.getController();
+ cardController.setOrganization(charity);
+
+ cardsContainer.getChildren().add(card);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not load organization card.", e);
+ }
+ }
+ }
+
+ /**
+ * This method is used to set the initial search query in the search field.
+ *
+ * @param query is the initial search query.
+ */
+ @FXML
+ public void setInitialSearch(String query) {
+ if (query == null || query.isBlank()) {
+ return;
+ }
+
+ searchField.setText(query);
+ }
+
+
+ /**
+ * This method is used to switch to the charity page for the selected charity.
+ *
+ * @param event action event from button click
+ */
+ public void switchToCharityPage(ActionEvent event) {
+ LoaderScene.LoadScene("CharityPage", event, charity, null);
+ }
+
+ /**
+ * This method is used to switch to the donation page.
+ *
+ * @param event action event from button click
+ */
+ @FXML
+ public void switchToDonationPage(ActionEvent event) {
+ LoaderScene.LoadScene("DonationPage", event, charity, null);
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java
index 8eb853f6..121ad3be 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/CharityPageController.java
@@ -3,6 +3,8 @@
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import ntnu.systemutvikling.team6.controller.components.LoaderScene;
import ntnu.systemutvikling.team6.models.Charity;
/**
@@ -10,6 +12,8 @@
* to donate to it. It also has a button to return to the front page.
*/
public class CharityPageController {
+ @FXML private TextField charitySearchField;
+
@FXML private Label CharityDescription;
@FXML private Label CharityName;
@@ -37,25 +41,31 @@ public void setCharity(Charity charity) {
CharityName.setText(charity.getName());
}
+
/**
- * This method is used to switch to the front page.
+ * This method is used to switch to the donation page.
*
* @param event
*/
@FXML
- public void switchToFrontPage(ActionEvent event) {
+ public void switchToDonationPage(ActionEvent event) {
System.out.println("Click");
- LoaderScene.LoadScene("FrontPage", event, charity);
+ LoaderScene.LoadScene("donationPage", event, charity, null);
}
/**
- * This method is used to switch to the donation page.
+ * This method is used to search for charities based on the input in the search field.
*
- * @param event
+ * @param event is the event that triggered the search.
*/
@FXML
- public void switchToDonationPage(ActionEvent event) {
- System.out.println("Click");
- LoaderScene.LoadScene("donationPage", event, charity);
+ public void handleSearch(ActionEvent event) {
+ String query = charitySearchField.getText().trim();
+
+ if (query.isEmpty()) {
+ return;
+ }
+
+ LoaderScene.LoadScene("availableOrganization", event, null, query);
}
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java
index 839a3e85..4a69ed54 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/DonationPageController.java
@@ -7,8 +7,11 @@
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
-import ntnu.systemutvikling.team6.DAO.DonationDAO;
+import ntnu.systemutvikling.team6.controller.components.LoaderScene;
+import ntnu.systemutvikling.team6.database.DAO.DonationDAO;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
import ntnu.systemutvikling.team6.models.Charity;
+import ntnu.systemutvikling.team6.models.user.User;
/**
* This controller represents the donation page, where the user can enter a donation amount and
@@ -21,6 +24,10 @@ public class DonationPageController {
@FXML private Label CharityName;
+ @FXML private TextField donationSearchField;
+
+ private DonationDAO donationSender = new DonationDAO(new DatabaseConnection());
+
/**
* Initialize method for the donation page. Sets the charity name label to the name of the charity
* that is being donated to. The charity is set from the original page it was called from when the
@@ -41,7 +48,7 @@ public void setCharity(Charity charity) {
* @param event
*/
public void switchToFrontPage(ActionEvent event) {
- LoaderScene.LoadScene("FrontPage", event, null);
+ LoaderScene.LoadScene("FrontPage", event, null, null);
}
/**
@@ -50,7 +57,7 @@ public void switchToFrontPage(ActionEvent event) {
* @param event
*/
public void switchToCharityPage(ActionEvent event) {
- LoaderScene.LoadScene("charityPage", event, charity);
+ LoaderScene.LoadScene("charityPage", event, charity, null);
}
/**
@@ -95,13 +102,13 @@ public void Donate(ActionEvent event) {
if (result.isPresent() && result.get() == ButtonType.OK) {
// Process donation
- processDonation(charity, amount);
+ //processDonation(charity, user, amount);
showAlert(
Alert.AlertType.INFORMATION,
"Thank you!",
"You have donated " + amount + " to " + charity.getName());
donatioAmount.clear();
- LoaderScene.LoadScene("FrontPage", event, null);
+ LoaderScene.LoadScene("FrontPage", event, null, null);
}
}
@@ -112,8 +119,8 @@ public void Donate(ActionEvent event) {
* @param charity
* @param amount
*/
- public void processDonation(Charity charity, double amount) {
- DonationDAO.addDonation(charity, amount);
+ public void processDonation(Charity charity, User user, double amount) {
+ donationSender.addDonation(charity, user, amount);
}
/**
@@ -130,4 +137,20 @@ private void showAlert(Alert.AlertType type, String title, String message) {
alert.setContentText(message);
alert.showAndWait();
}
+
+ /**
+ * This method is used to handle the search action when the user clicks the search button.
+ *
+ * @param event is the event that triggered the search.
+ */
+ @FXML
+ public void handleSearch(ActionEvent event) {
+ String query = donationSearchField.getText().trim();
+
+ if (query.isEmpty()) {
+ return;
+ }
+
+ LoaderScene.LoadScene("availableOrganization", event, null, query);
+ }
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java
index f8ffc2a7..b5b45226 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/FrontpageController.java
@@ -1,17 +1,28 @@
package ntnu.systemutvikling.team6.controller;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Random;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
+import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;
-import ntnu.systemutvikling.team6.database.DatabaseManager;
+import ntnu.systemutvikling.team6.controller.components.BaseController;
+import ntnu.systemutvikling.team6.controller.components.LoaderScene;
+import ntnu.systemutvikling.team6.controller.components.NavbarFooterController;
+import ntnu.systemutvikling.team6.controller.components.OrganizationCardController;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.database.Readers.CharitySelect;
+import ntnu.systemutvikling.team6.database.Readers.DonationSelect;
import ntnu.systemutvikling.team6.models.Charity;
-import ntnu.systemutvikling.team6.models.CharityRegistry;
import ntnu.systemutvikling.team6.models.Donation;
-import ntnu.systemutvikling.team6.models.DonationRegistry;
+import ntnu.systemutvikling.team6.models.registry.CharityRegistry;
+import ntnu.systemutvikling.team6.models.registry.DonationRegistry;
/**
* Landing page's controller. This is the first page the user sees when they open the application.
@@ -20,82 +31,80 @@
* it, or click on the featured charity to see more details about it. It also has buttons to switch
* to the charity page and the donation page for the featured charity
*/
-public class FrontpageController {
+public class FrontpageController extends BaseController implements NavbarFooterController {
@FXML private Charity featuredCharity;
-
@FXML private FlowPane cardsContainer;
-
@FXML private Label Carosel_Organisasjon;
-
@FXML private Label Carosel_Beskrivelse;
-
@FXML private Label Total_Orgnisasjon;
-
@FXML private Label Total_Donations;
-
@FXML private Label PreApproved_Percentage;
+ @FXML private TextField frontSearchField;
+ @FXML private CheckBox verifiedFilter;
+ @FXML private CheckBox childrenFilter;
+ @FXML private CheckBox healthFilter;
+ @FXML private CheckBox emergencyAidFilter;
+
+ private List allCharities = new ArrayList<>();
+
+ @Override
+ protected void authTokenisSet(){
+
+ }
/**
* Initialize method for the front page. This method is called when the front page is loaded. It
* retrieves the list of charities and donations from the database. The list of charities is
* displayed as a list of cards, where each card represents a charity from the
- * Innsamlingskontrollen A random charity is selected to be featured on the page, and its name and
- * description are displayed in the carousel section. The total number of charities, total amount
- * of donations, and percentage of pre-approved charities are also displayed on the page.
+ * Innsamlingskontrollen. A random charity is selected to be featured on the page, and its name
+ * and description are displayed in the carousel section. The total number of charities, total
+ * amount of donations, and percentage of pre-approved charities are also displayed on the page.
*/
@FXML
public void initialize() {
try {
- DatabaseManager db = new DatabaseManager();
- CharityRegistry Charities = db.getCharitiesFromDB();
- DonationRegistry Donations = db.getDonationFromDB();
- for (Charity ch : Charities.getAllCharities()) {
-
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/organizationCard.fxml"));
-
- Parent card = loader.load();
-
- OrganizationCardController cardController = loader.getController();
+ DatabaseConnection conn = new DatabaseConnection();
+ CharitySelect cdb = new CharitySelect(conn);
+ DonationSelect ddb = new DonationSelect(conn);
+ CharityRegistry charities = cdb.getCharitiesFromDB();
+ DonationRegistry donations = ddb.getDonationFromDB();
- // System.out.println("Added Name: " + ch.getName() + " Added Description: " +
- // ch.getDescription());
- cardController.setOrganization(ch);
+ allCharities = new ArrayList<>(charities.getAllCharities());
+ displayCharities(allCharities);
- cardsContainer.getChildren().add(card);
- }
- int Charities_size = Charities.getAllCharities().size();
+ int charitiesSize = charities.getAllCharities().size();
Random random = new Random();
- int randomIndex = random.nextInt(Charities_size);
- Charity randomCharity = Charities.getAllCharities().get(randomIndex);
+ int randomIndex = random.nextInt(charitiesSize);
+ Charity randomCharity = charities.getAllCharities().get(randomIndex);
this.featuredCharity = randomCharity;
Carosel_Organisasjon.setText(randomCharity.getName());
Carosel_Beskrivelse.setText(randomCharity.getDescription());
- Total_Orgnisasjon.setText(Integer.toString(Charities_size));
+ Total_Orgnisasjon.setText(Integer.toString(charitiesSize));
Total_Donations.setText(
Double.toString(
- Donations.getAllDonations().stream().mapToDouble(Donation::getAmount).sum()));
+ donations.getAllDonations().stream().mapToDouble(Donation::getAmount).sum()));
PreApproved_Percentage.setText(
String.format(
"%.2f",
- Charities.getAllCharities().stream().filter(Charity::getPreApproved).count()
+ charities.getAllCharities().stream().filter(Charity::getPreApproved).count()
* 100.0
- / Charities_size)
+ / charitiesSize)
+ "%");
-
} catch (Exception e) {
e.printStackTrace();
}
}
+
/**
* This method is used to switch to the charity page for the selected charity
*
* @param event
*/
public void switchToCharityPage(ActionEvent event) {
- LoaderScene.LoadScene("CharityPage", event, featuredCharity);
+ LoaderScene.LoadScene("CharityPage", event, featuredCharity, null);
}
/**
@@ -104,6 +113,121 @@ public void switchToCharityPage(ActionEvent event) {
* @param event
*/
public void switchToDonationPage(ActionEvent event) {
- LoaderScene.LoadScene("DonationPage", event, featuredCharity);
+ LoaderScene.LoadScene("DonationPage", event, featuredCharity, null);
+ }
+
+ /**
+ * This method is used to filter the charities based on the selected filters.
+ *
+ * @param event is the event that triggered the filter.
+ */
+ @FXML
+ public void handleCategoryFilterChange(ActionEvent event) {
+ displayCharities(getFilteredCharities());
+ }
+
+ /**
+ * This method is used to search for charities based on the input in the search field.
+ *
+ * @param event is the event that triggered the search.
+ */
+ @FXML
+ public void handleFrontSearch(ActionEvent event) {
+ String query = frontSearchField.getText().trim();
+
+ if (query.isEmpty()) {
+ return;
+ }
+
+ LoaderScene.LoadScene("availableOrganization", event, null, query);
+ }
+
+ /**
+ * This method is used to filter the charities based on the selected filters.
+ *
+ * @return a list of filtered charities.
+ */
+ private List getFilteredCharities() {
+ if (!verifiedFilter.isSelected()
+ && !childrenFilter.isSelected()
+ && !healthFilter.isSelected()
+ && !emergencyAidFilter.isSelected()) {
+ return allCharities;
+ }
+
+ List filteredCharities = new ArrayList<>();
+ for (Charity charity : allCharities) {
+ if (matchesSelectedFilters(charity)) {
+ filteredCharities.add(charity);
+ }
+ }
+ return filteredCharities;
+ }
+
+ /**
+ * This method is used to check if a charity matches the selected filters.
+ *
+ * @param charity is the charity to be checked.
+ * @return true if the charity matches the selected filters, false otherwise.
+ */
+ private boolean matchesSelectedFilters(Charity charity) {
+ return (verifiedFilter.isSelected() && charity.getPreApproved())
+ || (childrenFilter.isSelected() && matchesKeywordCategory(charity, "children"))
+ || (healthFilter.isSelected() && matchesKeywordCategory(charity, "health"))
+ || (emergencyAidFilter.isSelected() && matchesKeywordCategory(charity, "emergency"));
+ }
+
+ /**
+ * This method is used to check if a charity matches a specific category.
+ *
+ * @param charity is the charity to be checked.
+ * @param category is the category to check against.
+ * @return true if the charity matches the category, false otherwise.
+ */
+ private boolean matchesKeywordCategory(Charity charity, String category) {
+ String text = (charity.getName() + " " + charity.getDescription()).toLowerCase();
+
+ return switch (category) {
+ case "children" ->
+ text.contains("child")
+ || text.contains("children")
+ || text.contains("barn")
+ || text.contains("youth")
+ || text.contains("young");
+ case "health" ->
+ text.contains("health")
+ || text.contains("medical")
+ || text.contains("helse")
+ || text.contains("hospital")
+ || text.contains("care");
+ case "emergency" ->
+ text.contains("emergency")
+ || text.contains("relief")
+ || text.contains("crisis")
+ || text.contains("aid")
+ || text.contains("disaster");
+ default -> false;
+ };
+ }
+
+ /**
+ * This method is used to display the charities in the cards container.
+ *
+ * @param charities is the list of charities to be displayed.
+ */
+ private void displayCharities(List charities) {
+ cardsContainer.getChildren().clear();
+
+ for (Charity charity : charities) {
+ try {
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/components/organizationCard.fxml"));
+ Parent card = loader.load();
+ OrganizationCardController cardController = loader.getController();
+ cardController.setOrganization(charity);
+ cardsContainer.getChildren().add(card);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not load organization card.", e);
+ }
+ }
}
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/BaseController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/BaseController.java
new file mode 100644
index 00000000..da4af23a
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/BaseController.java
@@ -0,0 +1,17 @@
+package ntnu.systemutvikling.team6.controller.components;
+
+import ntnu.systemutvikling.team6.service.AuthenticationService;
+
+public abstract class BaseController {
+ protected AuthenticationService authToken;
+
+ public void setAuthToken(AuthenticationService authToken){
+ this.authToken = authToken;
+ authTokenisSet();
+ };
+ protected void authTokenisSet(){} // Do stuff after authtoken is set, on each controller
+
+ protected boolean isLoggedin(){
+ return authToken.isLoggedin() && authToken != null;
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/LoaderScene.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/LoaderScene.java
similarity index 82%
rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/LoaderScene.java
rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/LoaderScene.java
index 83ace1ea..30236e9a 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/LoaderScene.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/LoaderScene.java
@@ -1,4 +1,4 @@
-package ntnu.systemutvikling.team6.controller;
+package ntnu.systemutvikling.team6.controller.components;
import java.io.IOException;
import java.util.Objects;
@@ -10,6 +10,9 @@
import javafx.scene.image.Image;
import javafx.stage.Stage;
import ntnu.systemutvikling.team6.HmHApplication;
+import ntnu.systemutvikling.team6.controller.AvailableOrganizationController;
+import ntnu.systemutvikling.team6.controller.CharityPageController;
+import ntnu.systemutvikling.team6.controller.DonationPageController;
import ntnu.systemutvikling.team6.models.Charity;
/**
@@ -28,7 +31,7 @@ public class LoaderScene {
* @param event
* @param charity
*/
- public static void LoadScene(String sceneName, ActionEvent event, Charity charity) {
+ public static void LoadScene(String sceneName, ActionEvent event, Charity charity, String query) {
try {
System.out.println(HmHApplication.class.getResource("/fxml/" + sceneName + ".fxml"));
FXMLLoader fxmlLoader =
@@ -45,6 +48,9 @@ public static void LoadScene(String sceneName, ActionEvent event, Charity charit
if (controller instanceof DonationPageController donationController) {
donationController.setCharity(charity);
}
+ if (controller instanceof AvailableOrganizationController availableOrganizationController) {
+ availableOrganizationController.setInitialSearch(query);
+ }
Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
Image icon =
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/NavbarFooterController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/NavbarFooterController.java
new file mode 100644
index 00000000..0075893f
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/NavbarFooterController.java
@@ -0,0 +1,33 @@
+package ntnu.systemutvikling.team6.controller.components;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.TextField;
+
+public interface NavbarFooterController {
+
+ default void switchToFrontPage(ActionEvent event) {
+ System.out.println("Click!");
+ LoaderScene.LoadScene("FrontPage", event, null, null);
+ }
+
+ default void switchToAboutPage(ActionEvent event) {
+ System.out.println("Click!");
+ LoaderScene.LoadScene("aboutPage", event, null, null);
+ }
+
+ default void switchToProfilePage(ActionEvent event) {
+ System.out.println("Click!");
+ LoaderScene.LoadScene("aboutPage", event, null, null);
+ }
+
+ default void handleSearch(ActionEvent event) {
+ System.out.println("Click!");
+ LoaderScene.LoadScene("aboutPage", event, null, null);
+ }
+
+ default void switchToLoginPage(ActionEvent event) {
+ System.out.println("Click!");
+ LoaderScene.LoadScene("aboutPage", event, null, null);
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/OrganizationCardController.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/OrganizationCardController.java
similarity index 83%
rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/OrganizationCardController.java
rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/OrganizationCardController.java
index b51e530e..7644bbf8 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/OrganizationCardController.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/controller/components/OrganizationCardController.java
@@ -1,4 +1,4 @@
-package ntnu.systemutvikling.team6.controller;
+package ntnu.systemutvikling.team6.controller.components;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
@@ -27,10 +27,10 @@ public void setOrganization(Charity charity) {
/* EVENTS */
public void switchToCharityPage(ActionEvent event) {
- LoaderScene.LoadScene("CharityPage", event, charity);
+ LoaderScene.LoadScene("CharityPage", event, charity, null);
}
public void switchToDonationPage(ActionEvent event) {
- LoaderScene.LoadScene("DonationPage", event, charity);
+ LoaderScene.LoadScene("DonationPage", event, charity, null);
}
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/DonationDAO.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/DonationDAO.java
similarity index 72%
rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/DonationDAO.java
rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/DonationDAO.java
index c99056f9..730accac 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/DAO/DonationDAO.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/DonationDAO.java
@@ -1,29 +1,34 @@
-package ntnu.systemutvikling.team6.DAO;
+package ntnu.systemutvikling.team6.database.DAO;
import java.sql.*;
import java.util.UUID;
import ntnu.systemutvikling.team6.database.DatabaseConnection;
import ntnu.systemutvikling.team6.models.Charity;
+import ntnu.systemutvikling.team6.models.user.User;
/**
* This class is responsible for sending concurrent information about the donation to the Donation
* Database. Usally called from the DonationPageController, where the user confirms their donation.
*/
public class DonationDAO {
- private static final DatabaseConnection connection = new DatabaseConnection();
+ private final DatabaseConnection connection;
- /**
+ public DonationDAO(DatabaseConnection connection) {
+ this.connection = connection;
+ }
+
+ /**
* Gets the total ammount of donations for a given charity, and sends it to the database throught
* MySQL.
*
* @param charity
* @param amount
*/
- public static void addDonation(Charity charity, double amount) {
+ public void addDonation(Charity charity, User user, double amount) {
String sql_query =
"""
- INSERT INTO Donations (UUID_Donations, amount, date, Charities_UUID_charities)
- VALUES (?, ?, ?, ?)
+ INSERT INTO Donations (UUID_Donations, amount, date, charity_id, user_id)
+ VALUES (?, ?, ?, ?, ?)
""";
try (Connection conn = connection.getMySqlConnection();
PreparedStatement ps = conn.prepareStatement(sql_query)) {
@@ -33,6 +38,8 @@ INSERT INTO Donations (UUID_Donations, amount, date, Charities_UUID_charities)
ps.setDouble(2, amount);
ps.setDate(3, new Date(System.currentTimeMillis()));
ps.setString(4, charity.getUUID().toString());
+ ps.setString(5, user.getId().toString());
+
ps.executeUpdate();
conn.commit();
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/UserDAO.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/UserDAO.java
new file mode 100644
index 00000000..2125a088
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DAO/UserDAO.java
@@ -0,0 +1,94 @@
+package ntnu.systemutvikling.team6.database.DAO;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.models.user.User;
+
+/**
+ * This class is responsible for sending concurrent information about the user to the User database,
+ * and user settings to the settings database.
+ *
+ * @author Robin Strand Prestmo
+ */
+public class UserDAO {
+
+ private final DatabaseConnection connection;
+
+ public UserDAO(DatabaseConnection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * Gets the user and settings information and sends it to the database through MySQL.
+ *
+ *
+ * @param user the user to be saved in the database.
+ * @throws RuntimeException if a database error occurs during the operation
+ * @return true or false based on if the register is a success or not
+ */
+ public boolean registerUser(User user) {
+ String userSql =
+ """
+ INSERT INTO User (
+ UUID_User,
+ user_displayname,
+ user_name,
+ user_email,
+ user_password,
+ role
+ )
+ VALUES (?, ?, ?, ?, ?, ?)
+ """;
+
+ String settingsSql =
+ """
+ INSERT INTO Settings (
+ User_UUID_User,
+ isAnonymous,
+ language,
+ lightmode
+ )
+ VALUES (?, ?, ?, ?)
+ """;
+
+ int psUserRows = 0;
+ int psSettingsRows = 0;
+
+ try (Connection conn = connection.getMySqlConnection()) {
+
+ conn.setAutoCommit(false);
+
+ try (PreparedStatement psUser = conn.prepareStatement(userSql)) {
+
+ psUser.setString(1, user.getId().toString());
+ psUser.setString(2, user.getDisplayName()); // display name
+ psUser.setString(3, user.getUsername()); // username
+ psUser.setString(4, user.getEmail());
+ psUser.setString(5, user.getPasswordHash());
+ psUser.setString(6, user.getRole().name());
+
+ psUserRows = psUser.executeUpdate();
+ }
+
+ try (PreparedStatement psSettings = conn.prepareStatement(settingsSql)) {
+
+ psSettings.setString(1, user.getId().toString());
+ psSettings.setBoolean(2, user.getSettings().isAnonymous());
+ psSettings.setString(3, user.getSettings().getLanguage().name());
+ psSettings.setBoolean(4, user.getSettings().isLightMode());
+
+ psSettingsRows = psSettings.executeUpdate();
+
+ }
+
+ conn.commit();
+
+ return psUserRows > 0 && psSettingsRows > 0;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java
deleted file mode 100644
index a4007a05..00000000
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseManager.java
+++ /dev/null
@@ -1,299 +0,0 @@
-package ntnu.systemutvikling.team6.database;
-
-import java.sql.*;
-import java.util.List;
-import java.util.UUID;
-import ntnu.systemutvikling.team6.models.Charity;
-import ntnu.systemutvikling.team6.models.CharityRegistry;
-import ntnu.systemutvikling.team6.models.Donation;
-import ntnu.systemutvikling.team6.models.DonationRegistry;
-import ntnu.systemutvikling.team6.scraper.APICharityData;
-
-/**
- * Manages the Database with MySQL tables and JDBC.
- *
- *
This class is responsible for creating the tables needed for the application, if not done
- * already and maintaining the {@code charities} table based on data retrieved from the IK API. It
- * is also responsible for retrieving the data from the database and sending it to the application
- * through the CharityRegistry and DonationRegistry. It is used by the FrontpageController to
- * retrieve the data needed to display the charities
- */
-public class DatabaseManager {
- private final DatabaseConnection connection;
-
- /**
- * Contractor for DatabaseManager. It uses a DatabaseConnection object that contains a connection
- * credentials.
- */
- public DatabaseManager() {
- this.connection = new DatabaseConnection();
- }
-
- /**
- * Connection test for the Database. Does a simple SELECT SQL query and returns either true og and
- * Exception if failed
- *
- * @return true if Sucsedd or SQLExepction if failed
- */
- public boolean testConnection() {
- try (Connection conn = connection.getMySqlConnection();
- Statement stmt = conn.createStatement()) {
-
- ResultSet rs = stmt.executeQuery("SELECT 1");
-
- if (rs.next()) {
- System.out.println("Database connection verified.");
- return true;
- }
-
- } catch (SQLException e) {
- System.out.println("Database connection failed.");
- e.printStackTrace();
- }
-
- return false;
- }
-
- /**
- * Creates the {@code Charities} and {@code Donations} tables if they do not already exist.
- *
- *
The table structure for Charities is based on fields from {@link APICharityData}.
- *
- * @throws RuntimeException if a {@link SQLException} occurs while creating the table
- */
- public void createTables() {
- String sql_query1 =
- """
- -- -----------------------------------------------------
- -- Table `HelpMeHelp`.`Charities`
- -- -----------------------------------------------------
- CREATE TABLE IF NOT EXISTS Charities (
- UUID_charities CHAR(36) PRIMARY KEY,
- org_number VARCHAR(255) NOT NULL,
- charity_name VARCHAR(255) NOT NULL,
- charity_link VARCHAR(255) NOT NULL,
- pre_approved TINYINT NOT NULL,
- status VARCHAR(255) NOT NULL,
- UNIQUE KEY unique_org_number (org_number)
- ) ENGINE=InnoDB;
-
-
- """;
- String sql_query2 =
- """
- -- -----------------------------------------------------
- -- Table `HelpMeHelp`.`Donations`
- -- -----------------------------------------------------
- CREATE TABLE IF NOT EXISTS Donations (
- `UUID_Donations` CHAR(36) NOT NULL,
- `amount` DECIMAL NOT NULL,
- `date` DATE NOT NULL,
- `Charities_UUID_charities` CHAR(36) NOT NULL,
- PRIMARY KEY (`UUID_Donations`),
- INDEX `fk_Donations_Charities_idx` (`Charities_UUID_charities` ASC) VISIBLE,
- CONSTRAINT `fk_Donations_Charities`
- FOREIGN KEY (`Charities_UUID_charities`)
- REFERENCES Charities (`UUID_charities`)
- ON DELETE CASCADE
- ON UPDATE CASCADE)
- ENGINE = InnoDB;
- """;
-
- try (Connection conn = connection.getMySqlConnection();
- Statement s = conn.createStatement()) {
-
- s.execute(sql_query1);
- s.execute(sql_query2);
- } catch (SQLException e) {
- e.printStackTrace();
- throw new RuntimeException("Error creating table.");
- }
- }
-
- /**
- * This method is used to verify the integrity of the data in the {@code charities} table and to
- * update it based on the data retrieved from the IK API. The param charities are retrieved from
- * the IK API through the APICharityData class. Called in initialize method in
- * HmHApplication.java, which is the main class of the application, to ensure that the data is up
- * to date when the application starts. Uses a a temp table to ensure that the data in the
- * database is consistent with the data from the API.
- *
- * @param charities
- */
- public void addAPIDataToTable(List charities) {
- Connection conn = null;
- try {
- conn = connection.getMySqlConnection();
- conn.setAutoCommit(false);
- String sql_query =
- """
- INSERT INTO Charities (UUID_charities, org_number, charity_name, charity_link, pre_approved, status)
- VALUES (?, ?, ?, ?, ?, ?)
- ON DUPLICATE KEY UPDATE
- charity_name = VALUES(charity_name),
- charity_link = VALUES(charity_link),
- pre_approved = VALUES(pre_approved),
- status = VALUES(status)
- """;
-
- try (PreparedStatement ps = conn.prepareStatement(sql_query)) {
- for (Charity charity : charities) {
- if (charity.getUUID() == null) {
- ps.setString(1, UUID.randomUUID().toString());
- } else {
- ps.setString(1, charity.getUUID().toString());
- }
-
- ps.setString(2, charity.getOrg_number().replaceAll("\\s", ""));
- ps.setString(3, charity.getName());
- ps.setString(4, charity.getDescription());
- ps.setBoolean(5, charity.getPreApproved()); // Description is the link
- ps.setString(6, charity.getStatus());
-
- ps.addBatch();
- }
- ps.executeBatch();
- }
-
- // -- Intergerty Check:
- String createTemp =
- """
- CREATE TEMPORARY TABLE temp_api_charities (
- org_number VARCHAR(20) PRIMARY KEY
- )
- """;
-
- try (PreparedStatement ps = conn.prepareStatement(createTemp)) {
- ps.execute();
- }
-
- String insertTemp = "INSERT IGNORE INTO temp_api_charities (org_number) VALUES (?)";
-
- try (PreparedStatement ps = conn.prepareStatement(insertTemp)) {
-
- for (Charity charity : charities) {
- ps.setString(1, charity.getOrg_number().replaceAll("\\s", ""));
- ps.addBatch();
- }
-
- ps.executeBatch();
- }
-
- String deleteSql =
- """
- DELETE FROM Charities c
- WHERE NOT EXISTS (
- SELECT 1
- FROM temp_api_charities t
- WHERE t.org_number = c.org_number
- )
- """;
-
- try (PreparedStatement ps = conn.prepareStatement(deleteSql)) {
- ps.executeUpdate();
- }
-
- conn.commit();
-
- } catch (SQLException e) {
- if (conn != null) {
- try {
- conn.rollback();
- } catch (SQLException ex) {
- ex.printStackTrace();
- }
- }
- e.printStackTrace();
-
- throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
- } finally {
- if (conn != null) {
- try {
- conn.setAutoCommit(true);
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- public CharityRegistry getCharitiesFromDB() {
- CharityRegistry registry = null;
- Connection conn = null;
- try {
- conn = connection.getMySqlConnection();
- String sql_query =
- "SELECT UUID_charities, org_number, charity_name, charity_link, pre_approved, status FROM Charities";
- Statement stmt = conn.createStatement();
- ResultSet rs = stmt.executeQuery(sql_query);
-
- registry = new CharityRegistry();
- while (rs.next()) {
- Charity charity =
- new Charity(
- rs.getString("UUID_charities"),
- rs.getString("org_number"),
- rs.getString("charity_link"),
- rs.getString("charity_name"),
- rs.getBoolean("pre_approved"),
- rs.getString("status"));
- registry.addCharity(charity);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
- }
- return registry;
- }
-
- public DonationRegistry getDonationFromDB() {
- DonationRegistry registry = null;
- Connection conn = null;
- try {
- conn = connection.getMySqlConnection();
- String sql_query =
- """
- SELECT
- d.UUID_Donations,
- d.amount,
- d.date,
- c.UUID_charities,
- c.org_number,
- c.charity_name,
- c.charity_link,
- c.pre_approved,
- c.status
- FROM Donations d
- JOIN Charities c
- ON d.Charities_UUID_charities = c.UUID_charities
- """;
- Statement stmt = conn.createStatement();
- ResultSet rs = stmt.executeQuery(sql_query);
-
- registry = new DonationRegistry();
- while (rs.next()) {
- Charity charity =
- new Charity(
- rs.getString("UUID_charities"),
- rs.getString("org_number"),
- rs.getString("charity_name"),
- rs.getString("charity_link"),
- rs.getBoolean("pre_approved"),
- rs.getString("status"));
-
- Donation donation =
- new Donation(
- rs.getString("UUID_Donations"),
- rs.getDouble("amount"),
- rs.getDate("date").toLocalDate(),
- charity);
- registry.addDonation(donation);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
- }
- return registry;
- }
-}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java
new file mode 100644
index 00000000..e809dd43
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/DatabaseSetup.java
@@ -0,0 +1,289 @@
+package ntnu.systemutvikling.team6.database;
+
+import java.sql.*;
+import ntnu.systemutvikling.team6.models.*;
+import ntnu.systemutvikling.team6.models.user.*;
+
+/**
+ * Manages the Database with MySQL tables and test connection.
+ *
+ *
This class object is able to create MySQL to ntnu localized database and able to
+ * testConnection to it.
+ */
+public class DatabaseSetup {
+ private final DatabaseConnection connection;
+
+ /**
+ * Contractor for DatabaseManager. It uses a DatabaseConnection object that contains a connection
+ * credentials.
+ */
+ public DatabaseSetup(DatabaseConnection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * Connection test for the Database. Does a simple SELECT SQL query and returns either true og and
+ * Exception if failed
+ *
+ * @return true if Sucsedd or SQLExepction if failed
+ */
+ public boolean testConnection() {
+ try (Connection conn = connection.getMySqlConnection();
+ Statement stmt = conn.createStatement()) {
+
+ ResultSet rs = stmt.executeQuery("SELECT 1");
+
+ if (rs.next()) {
+ System.out.println("Database connection verified.");
+ return true;
+ }
+
+ } catch (SQLException e) {
+ System.out.println("Database connection failed.");
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * Creates the {@code Charities} and {@code Donations} tables if they do not already exist.
+ *
+ * @throws RuntimeException if a {@link SQLException} occurs while creating the table
+ */
+ public void createTables() {
+ String charitiesTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `HelpMeHelp`.`Charities`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`Charities` (
+ `UUID_charities` CHAR(36) NOT NULL,
+ `org_number` VARCHAR(255) NOT NULL,
+ `pre_approved` TINYINT NOT NULL,
+ `status` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`UUID_charities`),
+ UNIQUE INDEX `org_number_UNIQUE` (`org_number` ASC) VISIBLE)
+ ENGINE = InnoDB;
+ """;
+ String donationsTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `HelpMeHelp`.`Donations`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`Donations` (
+ `UUID_Donations` CHAR(36) NOT NULL,
+ `amount` DECIMAL NOT NULL,
+ `date` DATE NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_Donations`),
+ INDEX `fk_Donations_Charities_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Donations_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Donations_Charities`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Donations_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ ENGINE = InnoDB;
+ """;
+ String userTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `apbaluna`.`User`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`User` (
+ `UUID_User` CHAR(36) NOT NULL,
+ `user_displayname` VARCHAR(255) NOT NULL,
+ `user_name` VARCHAR(255) NOT NULL,
+ `user_email` VARCHAR(255) NOT NULL,
+ `user_password` VARCHAR(255) NOT NULL,
+ `role` VARCHAR(45) NOT NULL,
+ PRIMARY KEY (`UUID_User`))
+ ENGINE = InnoDB;
+ """;
+
+ String settingsTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `apbaluna`.`Settings`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`Settings` (
+ `UUID_user` CHAR(36) NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `language` VARCHAR(45) NOT NULL,
+ `lightmode` TINYINT NOT NULL,
+ PRIMARY KEY (`UUID_user`),
+ INDEX `fk_Settings_User1_idx` (`UUID_user` ASC) VISIBLE,
+ CONSTRAINT `fk_Settings_User1`
+ FOREIGN KEY (`UUID_user`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ ENGINE = InnoDB;
+ """;
+
+ String messagesTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `apbaluna`.`Messages`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`Messages` (
+ `UUID_message` CHAR(36) NOT NULL,
+ `message_title` VARCHAR(255) NOT NULL,
+ `message_content` VARCHAR(255) NOT NULL,
+ `message_date` DATE NOT NULL,
+ `sender_user_id` CHAR(36) NULL,
+ `sender_charity_id` CHAR(36) NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_message`),
+ INDEX `fk_Messages_User1_idx` (`sender_user_id` ASC) VISIBLE,
+ INDEX `fk_Messages_Charities1_idx` (`sender_charity_id` ASC) VISIBLE,
+ INDEX `fk_Messages_User2_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Messages_User1`
+ FOREIGN KEY (`sender_user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_Charities1`
+ FOREIGN KEY (`sender_charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Messages_User2`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ ENGINE = InnoDB;
+ """;
+
+ String feedbackTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `apbaluna`.`Feedback`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`Feedback` (
+ `UUID_feedback` CHAR(36) NOT NULL,
+ `feedback_comment` VARCHAR(255) NOT NULL,
+ `feedback_date` DATE NOT NULL,
+ `isAnonymous` TINYINT NOT NULL,
+ `charity_id` CHAR(36) NOT NULL,
+ `user_id` CHAR(36) NOT NULL,
+ PRIMARY KEY (`UUID_feedback`),
+ INDEX `fk_Feedback_Charities1_idx` (`charity_id` ASC) VISIBLE,
+ INDEX `fk_Feedback_User1_idx` (`user_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Feedback_Charities1`
+ FOREIGN KEY (`charity_id`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Feedback_User1`
+ FOREIGN KEY (`user_id`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ ENGINE = InnoDB;
+ """;
+ String categoriesTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `apbaluna`.`Categories`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`Categories` (
+ `category_id` INT NOT NULL AUTO_INCREMENT,
+ `category` VARCHAR(255) NOT NULL,
+ PRIMARY KEY (`category_id`))
+ ENGINE = InnoDB;
+ """;
+
+ String charityCategoriesTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `apbaluna`.`Charity_Categories`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`Charity_Categories` (
+ `Categories_category_id` INT NOT NULL,
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Categories_category_id`, `Charities_UUID_charities`),
+ INDEX `fk_Categories_has_Charities_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ INDEX `fk_Categories_has_Charities_Categories1_idx` (`Categories_category_id` ASC) VISIBLE,
+ CONSTRAINT `fk_Categories_has_Charities_Categories1`
+ FOREIGN KEY (`Categories_category_id`)
+ REFERENCES `apbaluna`.`Categories` (`category_id`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Categories_has_Charities_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ ENGINE = InnoDB;
+ """;
+
+ String charityUserTable =
+ """
+ -- -----------------------------------------------------
+ -- Table `apbaluna`.`CharityUsers`
+ -- -----------------------------------------------------
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`CharityUsers` (
+ `Charities_UUID_charities` CHAR(36) NOT NULL,
+ `User_UUID_User` CHAR(36) NOT NULL,
+ PRIMARY KEY (`Charities_UUID_charities`, `User_UUID_User`),
+ INDEX `fk_Charities_has_User_User1_idx` (`User_UUID_User` ASC) VISIBLE,
+ INDEX `fk_Charities_has_User_Charities1_idx` (`Charities_UUID_charities` ASC) VISIBLE,
+ CONSTRAINT `fk_Charities_has_User_Charities1`
+ FOREIGN KEY (`Charities_UUID_charities`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION,
+ CONSTRAINT `fk_Charities_has_User_User1`
+ FOREIGN KEY (`User_UUID_User`)
+ REFERENCES `apbaluna`.`User` (`UUID_User`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ ENGINE = InnoDB;
+ """;
+ String charityVanityTable =
+ """
+ CREATE TABLE IF NOT EXISTS `apbaluna`.`CharityVanity` (
+ `UUID_charity` CHAR(36) NOT NULL,
+ `charity_name` VARCHAR(255) NOT NULL,
+ `charity_link` VARCHAR(255) NOT NULL,
+ `description` TEXT NULL,
+ `logoURL` TEXT NULL,
+ `key_values` TEXT NULL,
+ `logoBLOB` MEDIUMBLOB NULL,
+ INDEX `fk_CharityVanity_Charities1_idx` (`UUID_charity` ASC) VISIBLE,
+ PRIMARY KEY (`UUID_charity`),
+ CONSTRAINT `fk_CharityVanity_Charities1`
+ FOREIGN KEY (`UUID_charity`)
+ REFERENCES `apbaluna`.`Charities` (`UUID_charities`)
+ ON DELETE NO ACTION
+ ON UPDATE NO ACTION)
+ ENGINE = InnoDB;
+ """;
+
+ try (Connection conn = connection.getMySqlConnection();
+ Statement s = conn.createStatement()) {
+
+ s.execute(charitiesTable);
+ s.execute(userTable);
+ s.execute(donationsTable);
+ s.execute(settingsTable);
+ s.execute(messagesTable);
+ s.execute(feedbackTable);
+ s.execute(categoriesTable);
+ s.execute(charityCategoriesTable);
+ s.execute(charityUserTable);
+ s.execute(charityVanityTable);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Error creating table.");
+ }
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CharitySelect.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CharitySelect.java
new file mode 100644
index 00000000..cb39b223
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/CharitySelect.java
@@ -0,0 +1,197 @@
+package ntnu.systemutvikling.team6.database.Readers;
+
+import java.sql.*;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.models.Charity;
+import ntnu.systemutvikling.team6.models.Feedback;
+import ntnu.systemutvikling.team6.models.registry.CharityRegistry;
+import ntnu.systemutvikling.team6.models.user.Language;
+import ntnu.systemutvikling.team6.models.user.Settings;
+import ntnu.systemutvikling.team6.models.user.User;
+
+/**
+ * Data access class responsible for reading charity-related data from the database.
+ *
+ *
Provides methods to retrieve all charities (with their associated feedback and users) as well
+ * as feedback entries for a specific charity by UUID.
+ *
+ *
All queries are executed against a MySQL database via a {@link DatabaseConnection}.
+ */
+public class CharitySelect {
+ private final DatabaseConnection connection;
+
+ /**
+ * Constructs a new {@code CharitySelect} with the given database connection.
+ *
+ * @param connection the {@link DatabaseConnection} to use for executing queries; must not be
+ * {@code null}
+ */
+ public CharitySelect(DatabaseConnection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * Retrieves all charities from the database, including their associated feedback and the users
+ * who submitted each piece of feedback.
+ *
+ *
The query performs a LEFT JOIN between the {@code Charities}, {@code Feedback}, {@code
+ * User}, {@code CharityVanity}, and {@code category(s)} tables. Each unique charity is added once
+ * to the registry; any feedback rows found for that charity are appended to its feedback list.
+ *
+ *
Note: charities with no feedback and categories are still included in the result due to the
+ * LEFT JOIN.
+ *
+ * @return a {@link CharityRegistry} containing all charities found in the database, each
+ * populated with its associated {@link Feedback} objects (if any)
+ * @throws RuntimeException if a {@link SQLException} occurs while executing the query
+ */
+ public CharityRegistry getCharitiesFromDB() {
+ CharityRegistry registry = null;
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT
+ c.UUID_charities, c.org_number, c.pre_approved, c.status,
+ f.UUID_feedback, f.feedback_comment, f.feedback_date, f.isAnonymous, f.charity_id, f.user_id,
+ cv.charity_name, cv.charity_link, cv.description, cv.logoURL, cv.key_values, cv.logoBLOB,
+ u.UUID_user, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role,
+ cat.category
+ FROM Charities c
+ LEFT JOIN Feedback f ON f.charity_id = c.UUID_charities
+ LEFT JOIN User u ON f.user_id = u.UUID_user
+ LEFT JOIN Charity_Categories cc ON cc.Charities_UUID_charities = c.UUID_charities
+ LEFT JOIN Cat|egories cat ON cat.category_id = cc.Categories_category_id
+ INNER JOIN CharityVanity cv ON cv.UUID_charity = c.UUID_charities;
+ """;
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql_query);
+
+ Charity currentCharity = null;
+ String lastCharity = null;
+
+ Set seenFeedbackIds = new HashSet<>();
+
+ registry = new CharityRegistry();
+ while (rs.next()) {
+ String currentId = rs.getString("UUID_charities");
+
+ if (lastCharity == null || !currentId.equals(lastCharity)) {
+ currentCharity =
+ new Charity(
+ rs.getString("UUID_charities"),
+ rs.getString("org_number"),
+ rs.getString("charity_name"),
+ rs.getString("charity_link"),
+ rs.getString("status"),
+ rs.getBoolean("pre_approved"),
+ rs.getString("description"),
+ rs.getString("logoURL"),
+ rs.getString("key_values"),
+ rs.getBytes("logoBLOB"));
+ registry.addCharity(currentCharity);
+ lastCharity = currentId;
+ seenFeedbackIds.clear();
+ }
+
+ String categoryName = rs.getString("category");
+ if (categoryName != null & !currentCharity.getCategory().contains(categoryName)) {
+ currentCharity.getCategory().add(categoryName);
+ }
+
+ String feedbackId = rs.getString("UUID_feedback");
+ if (feedbackId != null && !seenFeedbackIds.contains(feedbackId)) {
+ seenFeedbackIds.add(feedbackId);
+ User userWithMinimalSettingsAndInbox =
+ new User(
+ rs.getString("UUID_User"),
+ rs.getString("user_displayname"),
+ rs.getString("user_name"),
+ rs.getString("user_email"),
+ rs.getString("user_password"),
+ rs.getString("role"));
+ userWithMinimalSettingsAndInbox.setSettings(new Settings(false, Language.ENGLISH, false));
+
+ Feedback feedback =
+ new Feedback(
+ rs.getString("UUID_feedback"),
+ userWithMinimalSettingsAndInbox,
+ rs.getString("feedback_comment"),
+ LocalDate.parse(rs.getString("feedback_date")));
+
+ currentCharity.getFeedbacks().add(feedback);
+ }
+ }
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ }
+ return registry;
+ }
+
+ /**
+ * A helper function that retrieves all feedback entries associated with a specific charity,
+ * identified by its UUID. Currently, has no use.
+ *
+ *
Each {@link Feedback} object is populated with the associated {@link User} (without settings
+ * or inbox data). The query uses a LEFT JOIN between the {@code Feedback} and {@code User}
+ * tables, filtered by {@code charity_id}.
+ *
+ * @param charity_uuid the UUID of the charity whose feedback should be retrieved; must not be
+ * {@code null}
+ * @return an {@link ArrayList} of {@link Feedback} objects for the given charity; returns an
+ * empty list if no feedback exists for that charity
+ * @throws RuntimeException if any exception occurs while executing the query, wrapping the
+ * original cause
+ */
+ public ArrayList getFeedbackforCharityUUID(String charity_uuid) {
+ ArrayList Feedbacks = new ArrayList<>();
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT
+ f.UUID_feedback, f.feedback_comment, f.feedback_date, f.isAnonymous, f.charity_id, f.user_id,
+ u.UUID_user, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role
+ FROM Feedback f
+ LEFT JOIN User u ON f.user_id = u.UUID_user
+ WHERE f.charity_id = ?;
+ """;
+ PreparedStatement stmt = conn.prepareStatement(sql_query);
+ stmt.setString(1, charity_uuid);
+ ResultSet rs = stmt.executeQuery();
+
+ while (rs.next()) {
+ User userWithSettingsAndNoInbox =
+ new User(
+ rs.getString("UUID_User"),
+ rs.getString("user_displayname"),
+ rs.getString("user_name"),
+ rs.getString("user_email"),
+ rs.getString("user_password"),
+ rs.getString("role"));
+ userWithSettingsAndNoInbox.setSettings(new Settings(false, Language.NORWEGIAN, false));
+ Feedback feedback =
+ new Feedback(
+ rs.getString("UUID_feedback"),
+ userWithSettingsAndNoInbox,
+ rs.getString("feedback_comment"),
+ LocalDate.parse(rs.getString("feedback_date")));
+ Feedbacks.add(feedback);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ } finally {
+ conn = null;
+ }
+ return Feedbacks;
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/DonationSelect.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/DonationSelect.java
new file mode 100644
index 00000000..fe4a5afe
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/DonationSelect.java
@@ -0,0 +1,103 @@
+package ntnu.systemutvikling.team6.database.Readers;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.models.Charity;
+import ntnu.systemutvikling.team6.models.Donation;
+import ntnu.systemutvikling.team6.models.registry.DonationRegistry;
+import ntnu.systemutvikling.team6.models.user.User;
+
+/**
+ * Data access class responsible for reading donation data from the database.
+ *
+ *
Retrieves donations along with their associated {@link Charity} by performing an INNER JOIN
+ * between the {@code Donations} and {@code Charities} tables. Only donations with a matching
+ * charity record are included. Donor ({@link User}) and {@code CharityVanity} details are
+ * intentionally excluded to keep this query lightweight — join those separately if richer data is
+ * needed.
+ *
+ *
Note: {@code CharityVanity} fields (name, link, description, logo) are NOT fetched here since
+ * they live in a separate table. The {@link Charity} objects returned will only contain the core
+ * fields present in the {@code Charities} table.
+ */
+public class DonationSelect {
+
+ /** The database connection used for all queries in this class. */
+ private final DatabaseConnection connection;
+
+ /**
+ * Constructs a new {@code DonationSelect} with the given database connection.
+ *
+ * @param connection the {@link DatabaseConnection} to use for executing queries; must not be
+ * {@code null}
+ */
+ public DonationSelect(DatabaseConnection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * Retrieves all donations from the database, each populated with its associated {@link Charity}.
+ *
+ *
The query performs an INNER JOIN between the {@code Donations} and {@code Charities} tables
+ * on the charity UUID foreign key. Donations without a matching charity are excluded from the
+ * result.
+ *
+ * @return a {@link DonationRegistry} containing all matched donations; never {@code null}, but
+ * may be empty if no rows are returned
+ * @throws RuntimeException if a {@link SQLException} occurs while executing the query
+ */
+ public DonationRegistry getDonationFromDB() {
+ DonationRegistry registry = null;
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT
+ d.UUID_Donations, d.amount, d.isAnonymous, d.date, d.charity_id, d.user_id,
+ c.UUID_charities, c.org_number, c,pre_approved, c.status,
+ u.UUID_User, u.user_displayname u.user_name, u.user_email, u.user_password, u.role
+ FROM Donations d
+ INNER JOIN Charities c ON d.charity_id = c.UUID_charities
+ INNER JOIN User u ON d.user_id = u.UUID_user
+ """;
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql_query);
+
+ registry = new DonationRegistry();
+ while (rs.next()) {
+ Charity charity =
+ new Charity(
+ rs.getString("UUID_charities"),
+ rs.getString("org_number"),
+ rs.getBoolean("pre_approved"),
+ rs.getString("status"));
+
+ User user =
+ new User(
+ rs.getString("UUID_User"),
+ rs.getString("user_displayname"),
+ rs.getString("user_name"),
+ rs.getString("user_email"),
+ rs.getString("user_password"),
+ rs.getString("role"));
+ Donation donation =
+ new Donation(
+ rs.getString("UUID_Donations"),
+ rs.getDouble("amount"),
+ rs.getDate("date").toLocalDate(),
+ charity,
+ user,
+ rs.getBoolean("isAnonymous"));
+ registry.addDonation(donation);
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ }
+ return registry;
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/UserSelect.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/UserSelect.java
new file mode 100644
index 00000000..4f467457
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/database/Readers/UserSelect.java
@@ -0,0 +1,383 @@
+package ntnu.systemutvikling.team6.database.Readers;
+
+import java.sql.*;
+import java.time.LocalDate;
+import java.util.UUID;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.models.registry.UserRegistry;
+import ntnu.systemutvikling.team6.models.user.*;
+import ntnu.systemutvikling.team6.security.PasswordHasher;
+
+/**
+ * Data access class responsible for reading user-related data from the database.
+ *
+ *
Provides methods to retrieve individual users (by credentials or UUID), all users, a user's
+ * settings, and a user's inbox. Queries use LEFT JOINs across the {@code User}, {@code Settings},
+ * and {@code Messages} tables to assemble fully populated {@link User} objects in a single round
+ * trip where possible.
+ */
+public class UserSelect {
+ /** The database connection used for all queries in this class. */
+ private final DatabaseConnection connection;
+
+ /**
+ * Constructs a new {@code UserSelect} with the given database connection.
+ *
+ * @param connection the {@link DatabaseConnection} to use for executing queries; must not be
+ * {@code null}
+ */
+ public UserSelect(DatabaseConnection connection) {
+ this.connection = connection;
+ }
+
+ public boolean isUsernameTaken(String username){
+ try (Connection conn = connection.getMySqlConnection();
+ Statement stmt = conn.createStatement()) {
+
+ String mysql =
+ """
+ SELECT UUID_User FROM User WHERE user_name = ?
+ """;
+ PreparedStatement statement = conn.prepareStatement(mysql);
+ statement.setString(1, username);
+ ResultSet rs = statement.executeQuery();
+
+ if (rs.next()) {
+ System.out.println("Username Taken already");
+ return true;
+ }
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+
+ /**
+ * Retrieves a single {@link User} from the database matching the given username and password.
+ *
+ *
The password is hashed via {@link PasswordHasher} before being compared against the stored
+ * value. If a matching user is found, their {@link Settings} (when present) and {@link Inbox}
+ * (including any {@link Message} objects) are also populated. Returns {@code null} if no matching
+ * user is found.
+ *
+ *
Note: the current SQL query compares both parameters against {@code
+ * user_password}; the {@code user_name} column is not yet included in the WHERE clause, which may
+ * be a bug.
+ *
+ * @param username the plain-text username to look up
+ * @param password the plain-text password; hashed internally before the query runs
+ * @return the matching {@link User} with settings and inbox populated, or {@code null} if no
+ * match is found
+ * @throws RuntimeException if a {@link SQLException} occurs while executing the query
+ */
+ public User getUserFromDBUsernameAndPassword(String username, String password) {
+ PasswordHasher hasher = new PasswordHasher();
+ String hashedpassword = hasher.getHashPassword(password);
+
+ User user = null;
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT
+ u.UUID_User, u.user_displayname, u.user_name, u.user_email, u.user_password, u.role,
+ s.User_UUID_User, s.isAnonymous, s.language, s.lightmode,
+ m.UUID_message, m.message_title, m.message_content, m.message_date, m.sender_user_id, m.sender_charity_id, m.user_id
+ FROM User u
+ LEFT JOIN Settings s ON u.UUID_User = s.User_UUID_user
+ LEFT JOIN Messages m ON u.UUID_User = m.user_id
+ WHERE u.user_name = ? AND u.user_password = ?;
+ """;
+ PreparedStatement stmt = conn.prepareStatement(sql_query);
+ stmt.setString(1, username);
+ stmt.setString(2, hashedpassword);
+
+ ResultSet rs = stmt.executeQuery();
+
+ String lastUserid = null;
+ while (rs.next()) {
+ String userId = rs.getString("UUID_User");
+ if (lastUserid == null || !userId.equals(lastUserid)) {
+ user =
+ new User(
+ userId,
+ rs.getString("user_displayname"),
+ rs.getString("user_name"),
+ rs.getString("user_email"),
+ rs.getString("user_password"),
+ rs.getString("role"));
+ if (rs.getString("isAnonymous") != null) {
+ Settings settings =
+ new Settings(
+ rs.getBoolean("isAnonymous"),
+ Language.valueOf(rs.getString("language").toUpperCase()),
+ rs.getBoolean("lightmode"));
+ user.setSettings(settings);
+ }
+ user.setInbox(new Inbox());
+ lastUserid = userId;
+ }
+ String messageId = rs.getString("UUID_message");
+ if (messageId != null) {
+ Message message =
+ new Message(
+ rs.getString("message_title"),
+ UUID.fromString(rs.getString("sender_charity_id")),
+ rs.getString("message_content"),
+ LocalDate.parse(rs.getString("message_date")));
+
+ user.getInbox().addMessage(message);
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ } finally {
+ conn = null;
+ }
+ return user;
+ }
+
+ /**
+ * Retrieves a single {@link User} from the database by their UUID.
+ *
+ *
The returned user is fully populated with {@link Settings} (when present) and an {@link
+ * Inbox} containing any associated {@link Message} objects. Returns {@code null} if no user with
+ * the given UUID exists.
+ *
+ * @param user_id the UUID string of the user to retrieve; must not be {@code null}
+ * @return the matching {@link User} with settings and inbox populated, or {@code null} if no user
+ * is found
+ * @throws RuntimeException if a {@link SQLException} occurs while executing the query
+ */
+ public User getUserFromDBUuid(String user_id) {
+ User user = null;
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT
+ u.UUID_User, u.user_displayname u.user_name, u.user_email, u.user_password, u.role,
+ s.User_UUID_User, s.isAnonymous, s.language, s.lightmode,
+ m.UUID_message, m.message_title, m.message_content, m.message_date, m.sender_user_id, m.sender_charity_id, m.user_id
+ FROM User u
+ LEFT JOIN Settings s ON u.UUID_User = s.User_UUID_user
+ LEFT JOIN Messages m ON u.UUID_User = m.user_id
+ WHERE u.UUID_User = ?;
+ """;
+ PreparedStatement stmt = conn.prepareStatement(sql_query);
+ stmt.setString(1, user_id);
+ ResultSet rs = stmt.executeQuery();
+
+ String lastUserid = null;
+ while (rs.next()) {
+ String userId = rs.getString("UUID_User");
+ if (lastUserid == null || !userId.equals(lastUserid)) {
+ user =
+ new User(
+ userId,
+ rs.getString("user_displayname"),
+ rs.getString("user_name"),
+ rs.getString("user_email"),
+ rs.getString("user_password"),
+ rs.getString("role"));
+ if (rs.getString("isAnonymous") != null) {
+ Settings settings =
+ new Settings(
+ rs.getBoolean("isAnonymous"),
+ Language.valueOf(rs.getString("language").toUpperCase()),
+ rs.getBoolean("lightmode"));
+ user.setSettings(settings);
+ }
+ user.setInbox(new Inbox());
+ lastUserid = userId;
+ }
+ String messageId = rs.getString("UUID_message");
+ if (messageId != null) {
+ Message message =
+ new Message(
+ rs.getString("message_title"),
+ UUID.fromString(rs.getString("sender_charity_id")),
+ rs.getString("message_content"),
+ LocalDate.parse(rs.getString("message_date")));
+
+ user.getInbox().addMessage(message);
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ } finally {
+ conn = null;
+ }
+ return user;
+ }
+
+ /**
+ * Retrieves all users from the database, each fully populated with their {@link Settings} and
+ * {@link Inbox}.
+ *
+ *
The query LEFT JOINs {@code User}, {@code Settings}, and {@code Messages}. Multiple rows for
+ * the same user UUID (due to multiple messages) are collapsed into a single {@link User} object
+ * with all messages appended to its inbox.
+ *
+ * @return a {@link UserRegistry} containing all users found in the database; never {@code null},
+ * but may be empty if no users exist
+ * @throws RuntimeException if a {@link SQLException} occurs while executing the query
+ */
+ public UserRegistry getUsersFromDB() {
+ UserRegistry registry = new UserRegistry();
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT
+ u.UUID_User, u.user_displayname u.user_name, u.user_email, u.user_password, u.role,
+ s.User_UUID_User, s.isAnonymous, s.language, s.lightmode,
+ m.UUID_message, m.message_title, m.message_content, m.message_date, m.sender_user_id, m.sender_charity_id, m.user_id
+ FROM User u
+ LEFT JOIN Settings s ON u.UUID_User = s.User_UUID_user
+ LEFT JOIN Messages m ON u.UUID_User = m.user_id
+ """;
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery(sql_query);
+
+ User currentUser = null;
+ String lastUserid = null;
+
+ while (rs.next()) {
+ String userId = rs.getString("UUID_User");
+
+ if (lastUserid == null || !userId.equals(lastUserid)) {
+ currentUser =
+ new User(
+ userId,
+ rs.getString("user_displayname"),
+ rs.getString("user_name"),
+ rs.getString("user_email"),
+ rs.getString("user_password"),
+ rs.getString("role"));
+ if (rs.getString("isAnonymous") != null) {
+ Settings settings =
+ new Settings(
+ rs.getBoolean("isAnonymous"),
+ Language.valueOf(rs.getString("language").toUpperCase()),
+ rs.getBoolean("lightmode"));
+ currentUser.setSettings(settings);
+ }
+ currentUser.setInbox(new Inbox());
+ registry.addUser(currentUser);
+ lastUserid = userId;
+ }
+ String messageId = rs.getString("UUID_message");
+ if (messageId != null) {
+ Message message =
+ new Message(
+ rs.getString("message_title"),
+ UUID.fromString(rs.getString("sender_charity_id")),
+ rs.getString("message_content"),
+ LocalDate.parse(rs.getString("message_date")));
+
+ currentUser.getInbox().addMessage(message);
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ } finally {
+ conn = null;
+ }
+ return registry;
+ }
+
+ /**
+ * Retrieves the {@link Settings} for a specific user by their UUID.
+ *
+ *
At most one row is fetched (via {@code setMaxRows(1)}). Returns {@code null} if no settings
+ * row exists for the given user.
+ *
+ * @param user_id the UUID string of the user whose settings should be retrieved; must not be
+ * {@code null}
+ * @return the user's {@link Settings}, or {@code null} if none are found
+ * @throws RuntimeException if a {@link SQLException} occurs while executing the query
+ */
+ public Settings getSettingsForUser(String user_id) {
+ Settings settings = null;
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT User_UUID_User, isAnonymous, language, lightmode FROM Settings
+ WHERE UUID_user = ?;
+ """;
+ PreparedStatement stmt = conn.prepareStatement(sql_query);
+ stmt.setString(1, user_id);
+ stmt.setMaxRows(1);
+ ResultSet rs = stmt.executeQuery();
+
+ while (rs.next()) {
+ settings =
+ new Settings(
+ rs.getBoolean("isAnonymous"),
+ Language.valueOf(rs.getString("language").toUpperCase()),
+ rs.getBoolean("lightmode"));
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ } finally {
+ conn = null;
+ }
+
+ return settings;
+ }
+
+ /**
+ * Retrieves the {@link Inbox} for a specific user by their UUID, populated with all of their
+ * {@link Message} objects.
+ *
+ *
Returns an empty {@link Inbox} (never {@code null}) if no messages exist for the given user.
+ *
+ * @param user_id the UUID string of the user whose inbox should be retrieved; must not be {@code
+ * null}
+ * @return an {@link Inbox} containing all messages for the user; empty if no messages are found
+ * @throws RuntimeException if a {@link SQLException} occurs while executing the query
+ */
+ public Inbox getInboxForUser(String user_id) {
+ Inbox inbox = new Inbox();
+ Connection conn = null;
+ try {
+ conn = connection.getMySqlConnection();
+ String sql_query =
+ """
+ SELECT UUID_message, message_title, message_content, message_date, sender_user_id, sender_charity_id, user_id FROM Messages
+ WHERE user_id = ?;
+ """;
+ PreparedStatement stmt = conn.prepareStatement(sql_query);
+ stmt.setString(1, user_id);
+ ResultSet rs = stmt.executeQuery();
+
+ while (rs.next()) {
+ Message message =
+ new Message(
+ rs.getString("message_title"),
+ UUID.fromString(rs.getString("sender_charity_id")),
+ rs.getString("message_content"),
+ LocalDate.parse(rs.getString("message_date")));
+ inbox.addMessage(message);
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ } finally {
+ conn = null;
+ }
+ return inbox;
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java
index d97d9968..e019aead 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Charity.java
@@ -20,8 +20,8 @@ public class Charity {
/* Name of the charity */
private String name;
- /* Description of the charity's mission and activities */
- private String description;
+ /* URL of the charity */
+ private String url;
/* Is the charity verified? */
private String status;
@@ -29,11 +29,40 @@ public class Charity {
private boolean is_pre_approved;
/* Category for the charity */
- private String category;
+ private List category;
+
+ /* Description for the charity */
+ private String description;
+
+ /* URL for the logo of the charity */
+ private String logoURL;
+
+ /* Key values for the charity */
+ private String keyValues;
/* List that contains the charity's Feedbacks */
private List feedbacks;
+ /* Bytecode for the charity logo */
+ private byte[] logoBlob;
+
+ /**
+ * Minimal contructor JUST FOR DONATIONSSELECT. Just cause donation object needs to only contain
+ * information about receiver {@code Chairty } and donator {@code User}, and not necessarily Urls,
+ * logos, and etc.
+ *
+ * @param uuid from DonationSelect
+ * @param org_number matches from DonationSelect
+ * @param is_pre_approved name matches from DonationSelect
+ * @param status name matches from DonationSelect
+ */
+ public Charity(String uuid, String org_number, Boolean is_pre_approved, String status) {
+ this.UUID = java.util.UUID.fromString(uuid);
+ this.org_number = org_number.replaceAll("\\s", "");
+ this.is_pre_approved = is_pre_approved;
+ this.status = status;
+ }
+
/**
* Contructor for creating a new charity. Taylored to match data given from Api. Other attributes
* will just be initialized as empty
@@ -48,16 +77,17 @@ public Charity(
this.UUID = java.util.UUID.randomUUID();
this.org_number = org_number.replaceAll("\\s", "");
this.name = name;
- this.description = "Les mer her: " + link;
+ this.url = link;
this.is_pre_approved = is_pre_approved;
this.status = status;
this.feedbacks = new ArrayList<>();
- this.category = "";
+ this.category = new ArrayList<>();
}
/**
- * Contructor for creating a new charity. Taylored to match data given from DATABASE. Other
- * attributes will just be initialized as empty
+ * Contructor for creating a new charity. Taylored to match data given from DATABASE. Expects
+ * paramaters that will fill all attributes. EXECPT for feedbacks and categories (which is done
+ * right after).
*
* @param org_number matches from innsamlingkontrollen
* @param name matches from innsamlingkontrollen
@@ -67,18 +97,26 @@ public Charity(
public Charity(
String uuid,
String org_number,
- String link,
String name,
+ String url,
+ String status,
boolean is_pre_approved,
- String status) {
+ String description,
+ String logoURL,
+ String keyValues,
+ byte[] logblob) {
this.UUID = UUID.fromString(uuid);
this.org_number = org_number.replaceAll("\\s", "");
this.name = name;
- this.description = link;
+ this.url = url;
this.is_pre_approved = is_pre_approved;
this.status = status;
+ this.category = new ArrayList<>();
+ this.description = description;
+ this.logoURL = logoURL;
+ this.keyValues = keyValues;
this.feedbacks = new ArrayList<>();
- this.category = "";
+ this.logoBlob = logblob;
}
/** Getters for the charity's attributes. */
@@ -102,7 +140,7 @@ public List getFeedbacks() {
return feedbacks;
}
- public String getCategory() {
+ public List getCategory() {
return category;
}
@@ -110,8 +148,24 @@ public String getName() {
return name;
}
+ public String getURL() {
+ return this.url;
+ }
+
public String getDescription() {
- return description;
+ return this.description;
+ }
+
+ public String getLogoURL() {
+ return this.logoURL;
+ }
+
+ public String getKeyValues() {
+ return this.keyValues;
+ }
+
+ public byte[] getLogoBlob() {
+ return this.logoBlob;
}
/** Setter for verification status. This one sets the charity as verified. */
@@ -123,4 +177,34 @@ public void setVerified() {
public void setUnverified() {
this.status = "Veto";
}
+
+ /** Setter for categories. */
+ public void setCategory(List category) {
+ this.category = category;
+ }
+
+ /** Setter for description. */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /** Setter for the URL of the charity's logo. */
+ public void setLogoURL(String url) {
+ this.logoURL = url;
+ }
+
+ /** Setter for the charity's key values. */
+ public void setKeyValues(String values) {
+ this.keyValues = values;
+ }
+
+ /** Setter for the charity's logo Blob. */
+ public void setLogoBlob(byte[] logoBlob) {
+ this.logoBlob = logoBlob;
+ }
+
+ /** Setter for setting feedbacks */
+ public void setFeedbacks(ArrayList feedbacks) {
+ this.feedbacks = feedbacks;
+ }
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java
index 64d87331..c058688a 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Donation.java
@@ -6,7 +6,7 @@
public class Donation {
/* UUID for uniquely identifying each donation */
- private UUID charityId;
+ private UUID donationID;
/* Ammount of money donated */
private double amount;
@@ -33,7 +33,7 @@ public class Donation {
* @param donor
*/
public Donation(double amount, LocalDate date, Charity charity, User donor) {
- this.charityId = UUID.randomUUID();
+ this.donationID = UUID.randomUUID();
this.amount = amount;
this.date = date;
this.charity = charity;
@@ -42,21 +42,28 @@ public Donation(double amount, LocalDate date, Charity charity, User donor) {
}
/**
- * Constructor for creating a new donation. Taylored for getting info FROM DATABASE. NEEDS TO BE
- * CHANGED in phase 3.
+ * Constructor for creating a donation reed from the database.
*
- * @param amount
- * @param date
- * @param charity
- * @param uuid
+ * @param donationId the stored UUID string for this donation; must not be {@code null}
+ * @param amount the donated amount
+ * @param date the date the donation was made; must not be {@code null}
+ * @param charity the receiving charity; must not be {@code null}
+ * @param donor the donating user, or {@code null} if anonymous
+ * @param isAnonymous whether the donation was recorded as anonymous
*/
- public Donation(String uuid, double amount, LocalDate date, Charity charity) {
- this.charityId = UUID.fromString(uuid);
+ public Donation(
+ String donationId,
+ double amount,
+ LocalDate date,
+ Charity charity,
+ User donor,
+ boolean isAnonymous) {
+ this.donationID = UUID.fromString(donationId);
this.amount = amount;
this.date = date;
this.charity = charity;
- this.donor = null;
- this.isAnonymous = true;
+ this.donor = donor;
+ this.isAnonymous = isAnonymous;
}
/* Getters for the donation's attributes */
@@ -65,7 +72,11 @@ public boolean isAnonymous() {
}
public UUID getCharityId() {
- return charityId;
+ return charity.getUUID();
+ }
+
+ public UUID getDonationID() {
+ return donationID;
}
public double getAmount() {
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Feedback.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Feedback.java
index 490c47e6..6e78c3b2 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Feedback.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/Feedback.java
@@ -1,6 +1,6 @@
package ntnu.systemutvikling.team6.models;
-import java.time.LocalDateTime;
+import java.time.LocalDate;
import java.util.UUID;
import ntnu.systemutvikling.team6.models.user.User;
@@ -15,13 +15,13 @@ public class Feedback {
private String comment;
/* The date and time when the feedback was given */
- private LocalDateTime date;
+ private LocalDate date;
/* Is the feedback given anonymously? */
private boolean isAnonymous;
/**
- * Constructor for creating a new feedback.
+ * Constructor for creating a new feedback now.
*
* @param user The user who gives the feedback.
* @param comment The content of the feedback.
@@ -30,7 +30,22 @@ public Feedback(User user, String comment) {
this.feedbackId = UUID.randomUUID();
this.user = user;
this.comment = comment;
- this.date = LocalDateTime.now();
+ this.date = LocalDate.now();
+ this.isAnonymous = user.getSettings().isAnonymous();
+ }
+
+ /**
+ * Constructor for creating a new feedback, based on making a feedback previously made.
+ *
+ * @param user The user who gives the feedback.
+ * @param comment The content of the feedback.
+ * @param feedback_date The content of the feedback.
+ */
+ public Feedback(String feedback_id, User user, String feedback_comment, LocalDate feedback_date) {
+ this.feedbackId = UUID.fromString(feedback_id);
+ this.user = user;
+ this.comment = feedback_comment;
+ this.date = feedback_date;
this.isAnonymous = user.getSettings().isAnonymous();
}
@@ -48,7 +63,7 @@ public String getComment() {
return comment;
}
- public LocalDateTime getDate() {
+ public LocalDate getDate() {
return date;
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/UserRegistry.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/UserRegistry.java
deleted file mode 100644
index 514cbec1..00000000
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/UserRegistry.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ntnu.systemutvikling.team6.models;
-
-public class UserRegistry {}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/CharityRegistry.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/CharityRegistry.java
similarity index 93%
rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/CharityRegistry.java
rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/CharityRegistry.java
index 962b8338..c6aabbdd 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/CharityRegistry.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/CharityRegistry.java
@@ -1,6 +1,7 @@
-package ntnu.systemutvikling.team6.models;
+package ntnu.systemutvikling.team6.models.registry;
import java.util.*;
+import ntnu.systemutvikling.team6.models.Charity;
public class CharityRegistry {
private final List charities;
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/DonationRegistry.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/DonationRegistry.java
similarity index 81%
rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/DonationRegistry.java
rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/DonationRegistry.java
index b06009dd..d132a710 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/DonationRegistry.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/DonationRegistry.java
@@ -1,6 +1,7 @@
-package ntnu.systemutvikling.team6.models;
+package ntnu.systemutvikling.team6.models.registry;
import java.util.*;
+import ntnu.systemutvikling.team6.models.Donation;
public class DonationRegistry {
private final List donations;
@@ -18,7 +19,7 @@ public Optional findDonationById(UUID donationId) {
throw new IllegalArgumentException("DonationId can not be null.");
}
return donations.stream()
- .filter(donations -> donationId.equals(donations.getCharityId()))
+ .filter(donations -> donationId.equals(donations.getDonationID()))
.findFirst();
}
@@ -33,6 +34,6 @@ public boolean removeDonation(UUID donationId) {
if (donationId == null) {
throw new IllegalArgumentException("DonationId can not be null.");
}
- return donations.removeIf(donation -> donationId.equals(donation.getCharityId()));
+ return donations.removeIf(donation -> donationId.equals(donation.getDonationID()));
}
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/UserRegistry.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/UserRegistry.java
new file mode 100644
index 00000000..7aa85fb3
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/registry/UserRegistry.java
@@ -0,0 +1,37 @@
+package ntnu.systemutvikling.team6.models.registry;
+
+import java.util.*;
+import ntnu.systemutvikling.team6.models.user.User;
+
+public class UserRegistry {
+ private final List Users;
+
+ public UserRegistry() {
+ this.Users = new ArrayList<>();
+ }
+
+ public List getAllUsers() {
+ return Collections.unmodifiableList(Users);
+ }
+
+ public Optional findUserById(UUID userUUID) {
+ if (userUUID == null) {
+ throw new IllegalArgumentException("DonationId can not be null.");
+ }
+ return Users.stream().filter(u -> userUUID.equals(u.getId())).findFirst();
+ }
+
+ public void addUser(User user) {
+ if (user == null) {
+ throw new IllegalArgumentException("Donation can not be null.");
+ }
+ Users.add(user);
+ }
+
+ public boolean removeUserByUUID(UUID userUUID) {
+ if (userUUID == null) {
+ throw new IllegalArgumentException("DonationId can not be null.");
+ }
+ return Users.removeIf(user -> userUUID.equals(user.getId()));
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Language.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Language.java
index c568ede3..cbd66cf4 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Language.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Language.java
@@ -6,5 +6,6 @@
* @author Robin Strand Prestmo
*/
public enum Language {
+ NORWEGIAN,
ENGLISH
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Message.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Message.java
index b70ece1b..3a2a75d2 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Message.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Message.java
@@ -1,6 +1,6 @@
package ntnu.systemutvikling.team6.models.user;
-import java.time.LocalDateTime;
+import java.time.LocalDate;
import java.util.UUID;
// Enhetstester mangler
@@ -13,9 +13,9 @@
public class Message {
private final UUID id;
private final String title;
- private final String from;
+ private final UUID fromCharityID;
private final String content;
- private final LocalDateTime timeAndDate;
+ private final LocalDate timeAndDate;
/**
* Creates a message with a unique identifier. The message includes a title, a string who it's
@@ -26,13 +26,13 @@ public class Message {
* @param content the content of the message
* @throws IllegalArgumentException if title, from or content is null or blank.
*/
- public Message(String title, String from, String content) {
+ public Message(String title, UUID from, String content) {
if (title == null || title.isBlank()) {
throw new IllegalArgumentException("Title cannot be null or blank.");
}
- if (from == null || from.isBlank()) {
+ if (from == null) {
throw new IllegalArgumentException("From cannot be null or blank.");
}
@@ -42,9 +42,44 @@ public Message(String title, String from, String content) {
this.id = UUID.randomUUID();
this.title = title;
- this.from = from;
+ this.fromCharityID = from;
this.content = content;
- this.timeAndDate = LocalDateTime.now();
+ this.timeAndDate = LocalDate.now();
+ }
+
+ /**
+ * Creates a message with a unique identifier. The message includes a title, a string who it's
+ * from, content and the time and date. This one creates a message that has been created before.
+ *
+ * @param title the title of the message
+ * @param from who the message is from
+ * @param content the content of the message
+ * @param date date of when the message was created
+ * @throws IllegalArgumentException if title, from or content is null or blank.
+ */
+ public Message(String title, UUID from, String content, LocalDate date) {
+
+ if (title == null || title.isBlank()) {
+ throw new IllegalArgumentException("Title cannot be null or blank.");
+ }
+
+ if (from == null) {
+ throw new IllegalArgumentException("From cannot be null or blank.");
+ }
+
+ if (content == null || content.isBlank()) {
+ throw new IllegalArgumentException("Content cannot be null or blank.");
+ }
+
+ if (date == null) {
+ throw new IllegalArgumentException("Content cannot be null or blank.");
+ }
+
+ this.id = UUID.randomUUID();
+ this.title = title;
+ this.fromCharityID = from;
+ this.content = content;
+ this.timeAndDate = date;
}
public UUID getId() {
@@ -55,15 +90,15 @@ public String getTitle() {
return title;
}
- public String getFrom() {
- return from;
+ public UUID getFrom() {
+ return fromCharityID;
}
public String getContent() {
return content;
}
- public LocalDateTime getTimeAndDate() {
+ public LocalDate getTimeAndDate() {
return timeAndDate;
}
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java
index d8142836..086ddb9a 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/Settings.java
@@ -8,13 +8,13 @@
* @author Robin Strand Prestmo
*/
public class Settings {
- private boolean lightMode;
- private Language language;
private boolean anonymous;
+ private Language language;
+ private boolean lightMode;
/** Sets standard settings. LightMode enabled, language set to English, Anonymous disabled */
public Settings() {
- this(true, Language.ENGLISH, false);
+ this(false, Language.ENGLISH, true);
}
/**
@@ -24,7 +24,7 @@ public Settings() {
* @param language choose language
* @param anonymous choose if user is anonymous
*/
- public Settings(boolean lightMode, Language language, boolean anonymous) {
+ public Settings(boolean anonymous, Language language, boolean lightMode) {
if (language == null) {
throw new IllegalArgumentException("Language cannot be null");
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java
index ef590006..04fc6969 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/models/user/User.java
@@ -17,18 +17,19 @@ public class User {
private static final PasswordHasher passwordHasher = new PasswordHasher();
private final UUID id;
- private String name;
+ private String displayName;
+ private String username;
private String email;
private String passwordHash;
private final Role role;
- private final Settings settings;
- private final Inbox inbox;
+ private Settings settings;
+ private Inbox inbox;
/**
* Creates a new user.
*
- * @param id gives the user a unique identifier with UUID
- * @param name the name of the user
+ * @param displayName the name of the user that is shown
+ * @param username unqiue username used for login
* @param email the email of the user
* @param password the password for the user
* @param role users role
@@ -37,9 +38,12 @@ public class User {
* @throws IllegalArgumentException if any required argument is invalid.
*/
public User(
- String name, String email, String password, Role role, Settings settings, Inbox inbox) {
+ String displayName, String username, String email, String password, Role role, Settings settings, Inbox inbox) {
+ if (displayName == null || username.isBlank()){
+ throw new IllegalArgumentException("displayName cannot be null or blank.");
+ }
- if (name == null || name.isBlank()) {
+ if (username == null || username.isBlank()) {
throw new IllegalArgumentException("Name cannot be null or blank.");
}
@@ -61,7 +65,8 @@ public User(
}
this.id = UUID.randomUUID();
- this.name = name;
+ this.displayName = displayName;
+ this.username = username;
this.email = email;
this.passwordHash = passwordHasher.getHashPassword(password);
this.role = role;
@@ -69,20 +74,69 @@ public User(
this.inbox = inbox;
}
+ /**
+ * Creates a new user taylored for getting info from DATABASE. Settings and inbox can be set on a
+ * later date throught another method in databaseManager class
+ *
+ * @param uuid gives the user a unique identifier with UUID
+ * @param displayName the name of the user that is shown
+ * @param username the name of the user
+ * @param email the email of the user
+ * @param password the password for the user
+ * @param role users role
+ * @throws IllegalArgumentException if any required argument is invalid.
+ */
+ public User(String uuid, String displayName, String username, String email, String password, String role) {
+
+ if (uuid == null || uuid.isBlank()) {
+ throw new IllegalArgumentException("UUID cannot be null or blank.");
+ }
+
+ if (username == null || username.isBlank()) {
+ throw new IllegalArgumentException("Name cannot be null or blank.");
+ }
+
+ if (email == null || email.isBlank() || !email.contains("@") || !email.contains(".")) {
+ throw new IllegalArgumentException(
+ "Email cannot be null or blank," + " and must contain '@' and '.'");
+ }
+
+ if (role == null) {
+ throw new IllegalArgumentException("Role cannot be null");
+ }
+
+ this.id = UUID.fromString(uuid);
+ this.displayName = displayName;
+ this.username = username;
+ this.email = email;
+ this.passwordHash = password;
+ this.role = Role.valueOf(role);
+ this.settings = null;
+ this.inbox = null;
+ }
+
// Add Getters
public UUID getId() {
return id;
}
- public String getName() {
- return name;
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getUsername() {
+ return username;
}
public String getEmail() {
return email;
}
+ public String getPasswordHash() {
+ return passwordHash;
+ }
+
public Role getRole() {
return role;
}
@@ -103,11 +157,11 @@ public Inbox getInbox() {
* @param name the new name
* @throws IllegalArgumentException if the name is null or blank
*/
- public void setName(String name) {
+ public void setUsername(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("Name cannot be null or blank.");
}
- this.name = name;
+ this.username = name;
}
/**
@@ -146,4 +200,18 @@ public void setEmail(String email) {
public boolean checkPassword(String password) {
return passwordHasher.isValidPassword(password, passwordHash);
}
+
+ public void setSettings(Settings settings) {
+ if (settings == null) {
+ throw new IllegalArgumentException("Settings cannot be null");
+ }
+ this.settings = settings;
+ }
+
+ public void setInbox(Inbox inbox) {
+ if (inbox == null) {
+ throw new IllegalArgumentException("Inbox cannot be null");
+ }
+ this.inbox = inbox;
+ }
}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java
new file mode 100644
index 00000000..a69e3a9b
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/FullCharityScrape.java
@@ -0,0 +1,106 @@
+package ntnu.systemutvikling.team6.scraper;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import ntnu.systemutvikling.team6.models.Charity;
+import ntnu.systemutvikling.team6.models.registry.CharityRegistry;
+import ntnu.systemutvikling.team6.scraper.scraperComponents.APICharityScraper;
+import ntnu.systemutvikling.team6.scraper.scraperComponents.URLCharityScraper;
+
+/**
+ * Orchestrates a full charity data scrape by combining two data sources:
+ *
+ *
+ *
The external charity API (via {@link APICharityScraper}), which provides structured data
+ * such as organisation numbers, approval status, and charity URLs.
+ *
Individual charity web pages (via {@link URLCharityScraper}), which provide richer
+ * presentation data such as descriptions, logos, categories, and key values.
+ *
+ *
+ *
This class acts as a facade — callers only need to invoke {@link #getAPIAndURLCharityData()}
+ * to receive a fully populated {@link CharityRegistry}.
+ */
+public class FullCharityScrape {
+ private final APICharityScraper apiScraper;
+ private final LogoDownloader logoDownloader;
+
+ /**
+ * Constructs a {@code FullCharityScrape} instance and initialises the {@link APICharityScraper}
+ * with a new {@link HttpClient}.
+ *
+ * @throws URISyntaxException if the API endpoint URI used by {@link APICharityScraper} is
+ * malformed
+ */
+ public FullCharityScrape() throws URISyntaxException {
+ HttpClient https = HttpClient.newHttpClient();
+ this.apiScraper = new APICharityScraper(https);
+ this.logoDownloader = new LogoDownloader();
+ }
+
+ /**
+ * Performs a full two-phase scrape and returns a {@link CharityRegistry} populated with all
+ * available charity data.
+ *
+ *
Phase 1 — API scrape: Calls {@link APICharityScraper#checkConnection()} to verify
+ * availability, then fetches and parses the JSON payload into a {@link CharityRegistry}.
+ *
+ *
Phase 2 — URL scrape: Iterates over every {@link Charity} in the registry and uses a
+ * {@link URLCharityScraper} to enrich each entry with its description, logo URL, logo blob,
+ * categories, and key values scraped from the charity's own web page.
+ *
+ *
If {@link APICharityScraper#checkConnection()} throws an exception, it propagates to the
+ * caller and {@code null} is returned. If the connection check passes but returns {@code false},
+ * {@code null} is also returned.
+ *
+ * @return a fully populated {@link CharityRegistry}, or {@code null} if the API is unreachable
+ * @throws IOException if an I/O error occurs during the API request or URL scraping
+ * @throws InterruptedException if the HTTP request thread is interrupted
+ */
+ public CharityRegistry getAPIAndURLCharityData() throws IOException, InterruptedException {
+ try {
+ if (!apiScraper.checkConnection()) {
+ throw new RuntimeException("Connection check returned false");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ CharityRegistry charityRegistry = apiScraper.parseJSON(apiScraper.getJSONData());
+ int charityCounter = 0;
+
+ for (Charity charity : charityRegistry.getAllCharities()) {
+ System.out.println(charity.getName());
+ }
+ // Scrapes description, logo, categories, and key values from IK
+ for (Charity charity : charityRegistry.getAllCharities()) {
+ charityCounter++;
+
+ System.out.println(
+ "Scraping charity vanity details: "
+ + charityCounter
+ + " of "
+ + charityRegistry.getAllCharities().size());
+ try {
+ URLCharityScraper urlScraper = new URLCharityScraper(charity.getURL());
+ urlScraper.scrapeCharityPage();
+
+ charity.setDescription(urlScraper.getDescription());
+ charity.setCategory(urlScraper.getCategories());
+ charity.setLogoURL(urlScraper.getLogoURL());
+ charity.setKeyValues(urlScraper.getKeyValues());
+ byte[] logoBlob = LogoDownloader.downloadImageAsBlob(charity.getLogoURL());
+ charity.setLogoBlob(logoBlob);
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Failed to Scrape for: ["
+ + charityCounter
+ + "]: "
+ + charity.getName()
+ + ": "
+ + e.getMessage());
+ }
+ }
+ return charityRegistry;
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/LogoDownloader.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/LogoDownloader.java
new file mode 100644
index 00000000..2a871ffb
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/LogoDownloader.java
@@ -0,0 +1,54 @@
+package ntnu.systemutvikling.team6.scraper;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Facilitates downloading of .png images from the individual charity's page on IK, converting them
+ * to bytecode (blob), and then back to a .png.
+ */
+public class LogoDownloader {
+
+ /**
+ * Downloads a image from the given URL and converts it to a blob.
+ *
+ * @param imageUrl the URL of the image
+ * @return a blob containing the image data
+ */
+ public static byte[] downloadImageAsBlob(String imageUrl) {
+ if (imageUrl == null || imageUrl.isBlank()) return null;
+
+ try (InputStream in = new URL(imageUrl).openStream()) {
+ return in.readAllBytes();
+ } catch (Exception e) {
+ System.out.println("Error: Something went wrong when downloading the image.");
+ return null;
+ }
+ }
+
+ /**
+ * Converts a blob of image data back to a .png image file.
+ *
+ * @param imageBytes the blob containing the image data
+ * @param fileName the filename of the .png image file
+ */
+ public static void convertBlobToPNG(byte[] imageBytes, String fileName) {
+ if (imageBytes == null) {
+ return;
+ }
+ try {
+ Path folder = Paths.get("target", "logo");
+ Files.createDirectories(folder);
+
+ Path filePath = folder.resolve(fileName + ".png");
+
+ Files.write(filePath, imageBytes);
+
+ } catch (Exception e) {
+ System.out.println("Error: Something went wrong when converting blob to png.");
+ }
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityData.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityData.java
similarity index 94%
rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityData.java
rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityData.java
index 10a489d7..35b10d5d 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityData.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityData.java
@@ -1,6 +1,6 @@
-package ntnu.systemutvikling.team6.scraper;
+package ntnu.systemutvikling.team6.scraper.scraperComponents;
-import ntnu.systemutvikling.team6.database.DatabaseManager;
+import ntnu.systemutvikling.team6.database.DatabaseSetup;
/**
* Represents data parsed from the IK API JSON response. Instances are immutable; to update any
@@ -9,7 +9,7 @@
*
Receives data directly from {@link APICharityScraper}.
*
*
{@code org_number} should be a unique number, as it is used as a primary key in {@link
- * DatabaseManager}.
+ * DatabaseSetup}.
*/
public class APICharityData {
private final String org_number;
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityScraper.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraper.java
similarity index 93%
rename from helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityScraper.java
rename to helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraper.java
index 89422a1a..16be61f7 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/APICharityScraper.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/APICharityScraper.java
@@ -1,4 +1,4 @@
-package ntnu.systemutvikling.team6.scraper;
+package ntnu.systemutvikling.team6.scraper.scraperComponents;
import com.google.gson.Gson;
import java.io.IOException;
@@ -7,7 +7,7 @@
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import ntnu.systemutvikling.team6.models.Charity;
-import ntnu.systemutvikling.team6.models.CharityRegistry;
+import ntnu.systemutvikling.team6.models.registry.CharityRegistry;
/**
* Fetches JSON information from the IK API and parses the JSON into a list of {@link
@@ -81,6 +81,9 @@ public CharityRegistry parseJSON(String JSONData) {
CharityRegistry charityRegistry = new CharityRegistry();
for (APICharityData apiCharityData : charityData) {
+ if (apiCharityData.getStatus().equalsIgnoreCase("obs")) {
+ continue;
+ }
Charity charity =
new Charity(
apiCharityData.getOrg_number(),
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java
new file mode 100644
index 00000000..f9e98b37
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/scraper/scraperComponents/URLCharityScraper.java
@@ -0,0 +1,290 @@
+package ntnu.systemutvikling.team6.scraper.scraperComponents;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+/**
+ * Class for scraping the description, URL of the logo, string of categories, and key values of the
+ * charities registered in IK.
+ */
+public class URLCharityScraper {
+ private final String url;
+ private final WebDriver driver;
+ private String description;
+ private String logoURL;
+ private final List categories;
+ private final List keyValues;
+
+ /**
+ * Constructor used for production code.
+ *
+ *
It initializes the lists used for categories and keyValues, as well as defining the
+ * parameters used for the selenium Chromium-based browser that does the scraping.
+ *
+ * @param url the URL for the charity's webpage on IK
+ */
+ public URLCharityScraper(String url) {
+ this.categories = new ArrayList<>();
+ this.keyValues = new ArrayList<>();
+
+ ChromeOptions options = new ChromeOptions();
+ options.addArguments("--headless=new");
+ options.addArguments("--window-size=1920,1080");
+ options.addArguments("--disable-gpu");
+ options.addArguments("--no-sandbox");
+ options.addArguments("--disable-dev-shm-usage");
+
+ this.url = url;
+ this.driver = new ChromeDriver(options);
+ }
+
+ /**
+ * Constructor used for testing.
+ *
+ *
It accepts both a url (should ideally be a dud) and a {@link WebDriver} as parameters. The
+ * WebDriver is passed to make testing easier.
+ *
+ * @param url the URL for the charity's webpage on IK (for this constructor it should not be a
+ * real URL)
+ * @param driver the {@code WebDriver} object used for scraping
+ */
+ public URLCharityScraper(String url, WebDriver driver) {
+ this.categories = new ArrayList<>();
+ this.keyValues = new ArrayList<>();
+ this.url = url;
+ this.driver = driver;
+ }
+
+ /**
+ * Creates a {@link WebDriverWait} object for halting scraping until the correct pre-conditions
+ * are met.
+ *
+ * @return the {@code WebDriverWait} object to be used in the methods
+ */
+ protected WebDriverWait createWait() {
+ return new WebDriverWait(driver, Duration.ofSeconds(30));
+ }
+
+ /**
+ * Calls the {@code findElements} method from the {@code WebDriver} object and returns a list of
+ * the returned {@link WebElement} objects.
+ *
+ * @param by a selector for {@code WebElement} objects
+ * @return a list of found {@code WebElement} objects matching the given selector
+ */
+ public List findElements(By by) {
+ return driver.findElements(by);
+ }
+
+ /**
+ * Calls the {@code findElement} method from the {@code WebDriver} object and returns a list of
+ * the returned {@code WebElement} objects.
+ *
+ * @param by a selector for {@code WebElement} objects
+ * @return a list of found {@code WebElement} objects matching the given selector
+ */
+ protected WebElement findElement(By by) {
+ return driver.findElement(by);
+ }
+
+ /** Quits the driver instance, making it unusable. */
+ protected void closeDriver() {
+ driver.quit();
+ }
+
+ /** Scrapes the URL for the paragraphs containing the description of the charity. */
+ protected void updateDescription() {
+ try {
+ WebDriverWait wait = createWait();
+ StringBuilder descriptionString = new StringBuilder();
+
+ List readMoreLinks = findElements(By.cssSelector("a.read-more"));
+
+ if (!readMoreLinks.isEmpty()) {
+ WebElement readMore = findElement(By.cssSelector("a.read-more"));
+ readMore.click();
+
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".extra-info")));
+ }
+
+ wait.until(
+ ExpectedConditions.numberOfElementsToBeMoreThan(By.cssSelector(".information"), 0));
+
+ Thread.sleep(5000);
+ List firstDescription = findElements(By.cssSelector(".information"));
+
+ for (WebElement element : firstDescription) {
+ if (!element.getText().isBlank()) {
+ descriptionString.append(element.getText()).append("\n\n");
+ }
+ }
+
+ this.description = descriptionString.toString();
+
+ } catch (Exception e) {
+ System.out.println("No description found for " + driver.getCurrentUrl());
+ }
+ }
+
+ /** Scrapes the URL for the image URL of the logo for the charity. */
+ void updateLogo() {
+ try {
+ WebDriverWait wait = createWait();
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".logo > img")));
+ Thread.sleep(5000);
+
+ WebElement logo = findElement(By.cssSelector(".logo > img"));
+ this.logoURL = logo.getAttribute("src");
+
+ } catch (Exception e) {
+ System.out.println("No logo found for " + driver.getCurrentUrl());
+ }
+ }
+
+ /** Scrapes the URL for the category labels containing the categories for the charity. */
+ void updateCategories() {
+ try {
+ WebDriverWait wait = createWait();
+
+ wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".tag-label")));
+ Thread.sleep(5000);
+
+ List elements = findElements(By.cssSelector(".tag-label"));
+
+ for (WebElement element : elements) {
+ this.categories.add(element.getText());
+ }
+
+ } catch (Exception e) {
+ System.out.println("No categories found for " + driver.getCurrentUrl());
+ }
+ }
+
+ /**
+ * Scrapes the URL for the statistics of the charity; the percentage collected, the percentage
+ * that goes to the administration, and the percentage that is put towards the cause.
+ */
+ void updateKeyValues() {
+ try {
+ WebDriverWait wait = createWait();
+
+ String percentage;
+ WebElement element;
+
+ wait.until(
+ ExpectedConditions.visibilityOfElementLocated(
+ By.xpath(
+ "//li[.//h2[normalize-space()='Innsamlingsprosent']]//div[@class='graph']")));
+ Thread.sleep(5000);
+ element =
+ findElement(
+ By.xpath("//li[.//h2[normalize-space()='Innsamlingsprosent']]//div[@class='graph']"));
+ percentage = element.getAttribute("data-percentage");
+ this.keyValues.add(percentage);
+
+ wait.until(
+ ExpectedConditions.visibilityOfElementLocated(
+ By.xpath(
+ "//li[.//h2[normalize-space()='Administrasjonsprosent']]//div[@class='graph']")));
+
+ element =
+ findElement(
+ By.xpath(
+ "//li[.//h2[normalize-space()='Administrasjonsprosent']]//div[@class='graph']"));
+ percentage = element.getAttribute("data-percentage");
+ this.keyValues.add(percentage);
+
+ wait.until(
+ ExpectedConditions.visibilityOfElementLocated(
+ By.xpath("//li[.//h2[normalize-space()='Formålsprosent']]//div[@class='graph']")));
+
+ element =
+ findElement(
+ By.xpath("//li[.//h2[normalize-space()='Formålsprosent']]//div[@class='graph']"));
+ percentage = element.getAttribute("data-percentage");
+ this.keyValues.add(percentage);
+ } catch (Exception e) {
+ System.out.println("No key values found for " + driver.getCurrentUrl());
+ }
+ }
+
+ /** Runs all the scraper methods at once, updating the object parameters. */
+ public void scrapeCharityPage() {
+ try {
+ driver.get(this.url);
+ updateDescription();
+ updateLogo();
+ updateCategories();
+ updateKeyValues();
+ Thread.sleep(1000);
+
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } finally {
+ closeDriver();
+ }
+ }
+
+ /**
+ * Returns the description of the charity.
+ *
+ * @return a String containing the description of the charity.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns the URL of the logo for the charity.
+ *
+ * @return a String containing the URL for the logo of the charity.
+ */
+ public String getLogoURL() {
+ return logoURL;
+ }
+
+ /**
+ * Returns a String of the categories for the charity with ',' as a delimiter.
+ *
+ * @return a String of strings containing the categories for the charity
+ */
+ public List getCategories() {
+ /*
+ StringBuilder categoriesString = new StringBuilder();
+
+ for (int i = 0; i < this.categories.size(); i++) {
+ categoriesString.append(this.categories.get(i));
+ if (i < this.categories.size() - 1) {
+ categoriesString.append(",");
+ }
+ }
+ */
+ return categories;
+ }
+
+ /**
+ * Returns a String of the key value percentages for the charity with ':' as a delimiter, verified
+ * by IK.
+ *
+ * @return a String of the key values for the charity-
+ */
+ public String getKeyValues() {
+ StringBuilder keyValuesString = new StringBuilder();
+
+ for (int i = 0; i < this.keyValues.size(); i++) {
+ keyValuesString.append(this.keyValues.get(i));
+ if (i < this.keyValues.size() - 1) {
+ keyValuesString.append(":");
+ }
+ }
+ return keyValuesString.toString();
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/APIToDatabaseService.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/APIToDatabaseService.java
new file mode 100644
index 00000000..3edfce7d
--- /dev/null
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/APIToDatabaseService.java
@@ -0,0 +1,167 @@
+package ntnu.systemutvikling.team6.service;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+import ntnu.systemutvikling.team6.database.DatabaseConnection;
+import ntnu.systemutvikling.team6.models.Charity;
+
+public class APIToDatabaseService {
+ private final DatabaseConnection connection;
+
+ /**
+ * Contractor for APIToDatabaseService. It uses a DatabaseConnection object that contains a
+ * connection credentials.
+ *
+ * @param connection
+ */
+ public APIToDatabaseService(DatabaseConnection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * This method is used to verify the integrity of the data in the {@code charities} table and to
+ * update it based on the data retrieved from the IK API and the charity's URL. The param
+ * charities are retrieved from the IK API through the APICharityData class. Called in initialize
+ * method in HmHApplication.java, which is the main class of the application, to ensure that the
+ * data is up to date when the application starts. Uses a temp table to ensure that the data in
+ * the database is consistent with the data from the API.
+ *
+ *
Uses a URLScraper object to get data not contained in the API, and static methods from
+ * LogoDownloader to get the charity's logo as a blob.
+ *
+ * @param charities a list of {@code Charity} objects to add to the database
+ */
+ public void addAPIDataToTable(List charities) {
+ Connection conn = null;
+ // Everything charity related except categories
+ try {
+ conn = connection.getMySqlConnection();
+ conn.setAutoCommit(false);
+ String sql1 = """
+ INSERT INTO Charities (UUID_charities, org_number, pre_approved, status)
+ VALUES (?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE
+ pre_approved = VALUES(pre_approved),
+ status = VALUES(status);
+ """;
+
+ String sql2 = """
+ INSERT INTO CharityVanity (UUID_charity, charity_name, charity_link, description, logoURL, key_values, logoBlob)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE
+ charity_name = VALUES(charity_name),
+ charity_link = VALUES(charity_link),
+ description = VALUES(description),
+ logoURL = VALUES(logoURL),
+ key_values = VALUES(key_values),
+ logoBlob = VALUES(logoBlob);
+ """;
+
+ try (PreparedStatement ps1 = conn.prepareStatement(sql1);
+ PreparedStatement ps2 = conn.prepareStatement(sql2)) {
+
+ for (Charity charity : charities) {
+ String uuid = charity.getUUID() == null
+ ? UUID.randomUUID().toString()
+ : charity.getUUID().toString();
+
+ ps1.setString(1, uuid);
+ ps1.setString(2, charity.getOrg_number().replaceAll("\\s", ""));
+ ps1.setBoolean(3, charity.getPreApproved());
+ ps1.setString(4, charity.getStatus());
+ ps1.addBatch();
+
+ ps2.setString(1, uuid);
+ ps2.setString(2, charity.getName());
+ ps2.setString(3, charity.getURL());
+ ps2.setString(4, charity.getDescription());
+ ps2.setString(5, charity.getLogoURL());
+ ps2.setString(6, charity.getKeyValues());
+ ps2.setBytes(7, charity.getLogoBlob());
+ ps2.addBatch();
+ }
+
+ ps1.executeBatch();
+ ps2.executeBatch();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+
+ // -- Intergerty Check:
+ String createTemp =
+ """
+ CREATE TEMPORARY TABLE temp_api_charities (
+ org_number VARCHAR(255) PRIMARY KEY
+ )
+ """;
+
+ try (PreparedStatement ps = conn.prepareStatement(createTemp)) {
+ ps.execute();
+ }
+
+ String insertTemp = "INSERT IGNORE INTO temp_api_charities (org_number) VALUES (?)";
+
+ try (PreparedStatement ps = conn.prepareStatement(insertTemp)) {
+
+ for (Charity charity : charities) {
+ ps.setString(1, charity.getOrg_number().replaceAll("\\s", ""));
+ ps.addBatch();
+ }
+
+ ps.executeBatch();
+ }
+
+ String deleteSql =
+ """
+ DELETE FROM Charities c
+ WHERE NOT EXISTS (
+ SELECT 1 FROM temp_api_charities t
+ WHERE t.org_number = c.org_number
+ )
+ AND NOT EXISTS (
+ SELECT 1 FROM Donations d WHERE d.charity_id = c.UUID_charities
+ )
+ AND NOT EXISTS (
+ SELECT 1 FROM Feedback f WHERE f.charity_id = c.UUID_charities
+ )
+ AND NOT EXISTS (
+ SELECT 1 FROM CharityVanity cv WHERE cv.UUID_charity = c.UUID_charities
+ )
+ AND NOT EXISTS (
+ SELECT 1 FROM CharityUsers cu WHERE cu.Charities_UUID_charities = c.UUID_charities
+ );
+ """;
+
+ try (PreparedStatement ps = conn.prepareStatement(deleteSql)) {
+ ps.executeUpdate();
+ }
+
+ conn.commit();
+
+ } catch (SQLException e) {
+ if (conn != null) {
+ try {
+ conn.rollback();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+ }
+ e.printStackTrace();
+
+ throw new RuntimeException("ERROR: Something went wrong during updating charities table.");
+ } finally {
+ if (conn != null) {
+ try {
+ conn.setAutoCommit(true);
+ conn.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java
index 8b137891..ee780582 100644
--- a/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java
+++ b/helpmehelpapplication/src/main/java/ntnu/systemutvikling/team6/service/AuthenticationService.java
@@ -1 +1,117 @@
+package ntnu.systemutvikling.team6.service;
+import ntnu.systemutvikling.team6.database.DAO.UserDAO;
+import ntnu.systemutvikling.team6.database.Readers.UserSelect;
+import ntnu.systemutvikling.team6.models.user.Inbox;
+import ntnu.systemutvikling.team6.models.user.Role;
+import ntnu.systemutvikling.team6.models.user.Settings;
+import ntnu.systemutvikling.team6.models.user.User;
+
+
+/**
+ * Service class responsible for handling user authentication operations,
+ * including login, registration, and logout functionality.
+ *
+ * Maintains the state of the currently authenticated user throughout the session.
+ *
+ */
+public class AuthenticationService {
+ /** Handles read operations for user data from the database. */
+ private final UserSelect userDataReader;
+ /** Handles write operations for user data to the database. */
+ private final UserDAO userDataSender;
+
+ /** The currently authenticated user, or {@code null} if no user is logged in. */
+ private User currentUser;
+
+ /**
+ * Constructs an {@code AuthenticationService} with the specified data access objects.
+ *
+ * @param userDataReader the data reader used to query user information from the database
+ * @param userDataSender the DAO used to persist new user registrations to the database
+ */
+ public AuthenticationService(UserSelect userDataReader, UserDAO userDataSender) {
+ this.userDataReader = userDataReader;
+ this.userDataSender = userDataSender;
+ }
+
+ /**
+ * Attempts to authenticate a user with the given credentials.
+ *
+ * If a matching user is found in the database, they are set as the current user
+ * and the method returns {@code true}.
+ *
+ *
+ * @param username the username of the user attempting to log in
+ * @param password the password of the user attempting to log in
+ * @return {@code true} if authentication was successful; {@code false} otherwise
+ */
+ public boolean login(String username, String password){
+ User user = userDataReader.getUserFromDBUsernameAndPassword(username, password);
+
+ if (user != null){
+ currentUser = user;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Registers a new user account with the provided details.
+ *
+ * The new user is assigned the {@link Role#NORMAL_USER} role and default
+ * {@link Settings} and {@link Inbox}. Registration will fail if the
+ * username is already taken or if the database operation is unsuccessful.
+ * On success, the new user is set as the current user.
+ *