Compare commits

...
84 Commits
Author SHA1 Message Date
Casually9790 795ad8f699 Changed regex to basic email regex 2021-02-13 17:49:50 +01:00
catoandGitHub 470c722fca Update README.md 2021-02-13 17:49:20 +01:00
Casually9790 55be1d7b84 Privacy changes 2021-02-13 17:37:04 +01:00
catoandGitHub 1e40243d42 Update README.md 2021-02-13 17:34:13 +01:00
catoandGitHub 161959f798 Update README.md 2021-01-27 19:22:18 +01:00
Casually9790 cc8230fcee Merge branch 'testing' into main 2021-01-27 18:28:47 +01:00
Casually9790 5bba424c34 Merge remote-tracking branch 'origin/testing' into testing 2021-01-27 18:26:40 +01:00
Casually9790 f5e714883d Cleaning up for publication 2021-01-27 18:26:35 +01:00
catoandGitHub c890b66693 Update README.md 2021-01-27 18:26:05 +01:00
catoandGitHub c6e1a963be Update README.md 2021-01-27 18:21:29 +01:00
Casually9790 6cae7d78ae Updated Datelock 2021-01-17 16:19:26 +01:00
Casually9790 def3ed6309 Prepared everything for the votings 2021-01-17 15:54:32 +01:00
Casually9790 bbc83031b2 asdf3 2021-01-09 22:09:48 +01:00
Casually9790 57589e402c asdf2 2021-01-09 22:08:55 +01:00
Casually9790 fe5a91455c asdf 2021-01-09 22:07:45 +01:00
Casually9790 63aa795086 Final release 2021-01-09 21:56:17 +01:00
Casually9790 91874abe4e Added exeptions 2021-01-09 21:26:13 +01:00
Casually9790 0036b19721 reverse block 2021-01-09 21:09:46 +01:00
Casually9790 6ed12992c9 temp 2021-01-09 20:34:55 +01:00
Casually9790 865c3e778f BUGFIX 2021-01-09 20:23:16 +01:00
Casually9790 8da69f0170 Fixed out of bounds exception 2021-01-09 18:32:31 +01:00
Casually9790 7ee603d5df Added second voting 2021-01-09 02:35:55 +01:00
Casually9790 149a0dece3 Made final changes 2021-01-04 02:22:00 +01:00
Casually9790 937971da63 Expanded the number of candidates from 5 to 10-15 2021-01-04 00:42:43 +01:00
Casually9790 0b9d5cc90c Updated Requierments 2020-12-29 19:23:45 +01:00
Casually9790 880dd12a6f QOL 2020-12-27 23:33:56 +01:00
Casually9790 11e2906271 Added a datelock to end votes automatically 2020-12-27 23:23:41 +01:00
Casually9790 ffea848311 Changed to Voting Mode 2020-12-27 22:37:20 +01:00
Casually9790 d1c8f24d3d Fix error message 2020-12-18 20:11:28 +01:00
Casually9790 be8d49ff9e Final 2020-12-18 20:01:54 +01:00
Casually9790 506e2285fd End 2020-12-18 19:25:40 +01:00
Casually9790 14ab45227a Removed code pass through 2020-12-18 19:04:12 +01:00
Casually9790 8f65d9cc26 Finishing touches 2020-12-18 18:57:29 +01:00
Casually9790 cf138cd8cf Must function now Email is down 2020-12-17 03:11:28 +01:00
Casually9790 629d4076f8 fixed bug 2020-12-16 23:49:50 +01:00
Casually9790 b48453c300 test 2020-12-16 23:49:27 +01:00
Casually9790 7aff0c5b02 Test 2020-12-16 20:03:14 +01:00
Casually9790 e863d288ee Changed categories 2020-12-13 21:29:33 +01:00
Casually9790 3e69fcf97f Make input field static 2020-12-13 17:25:27 +01:00
Casually9790 97a1313524 Fixed email input bug 2020-12-13 17:20:42 +01:00
Casually9790 3be9c3b665 Css changes to start site 2020-12-13 17:18:56 +01:00
Casually9790 0cffad8ed0 Removed the dashboard because of the existence of phpmyadmin 2020-12-12 00:02:58 +01:00
Casually9790 5cb73a5a1a Regex ignores whitespaces now and added all fallback pages 2020-12-11 23:53:42 +01:00
Casually9790 9b9d754713 Merge remote-tracking branch 'origin/main' into main 2020-12-11 21:21:35 +01:00
Casually9790 7429462f91 added support of controling the behavoir of the program from the cli
Start application with:
-Dmotto for mottoVoting
-Dadding for addingPhase
-Dvoting for votingPhase
2020-12-11 10:57:58 +01:00
Casually9790 d629cec557 added 2020-12-11 00:20:17 +01:00
Casually9790 ec60234d7c added 2020-12-11 00:03:40 +01:00
Casually9790 0a33b55a61 Have to go to bed now need to write 2020-12-10 23:50:44 +01:00
Casually9790 e503b0df61 QOL changes
- Authentication code only gets send now if you don't have one or your code expired
- Added custom error page
- Trying out scalable Interface
2020-12-09 21:38:43 +01:00
Casually9790 aa036fb07b votingPhase can now be toggled from jar
Use -DvotingPhase={boolean} to set value (Defaults to false)
2020-12-08 23:32:20 +01:00
Casually9790 f0ada512ca Added dynamic resource parsing and fixed breaking bug 2020-12-08 23:03:14 +01:00
Casually9790 faa1cea8e2 Merge remote-tracking branch 'origin/main' into main 2020-12-07 23:42:40 +01:00
Casually9790 3186b63f35 Made the transition of data between possibleCandidates and Candidates easier
CHANGED VotingController:
    - changed: candidateRepositorys get now only created if empty and 'candidatesAdded == true'

CHANGED PossibleCandidate:
    - added: new getter getCategoryID

CHANGED PossibleCandidateRepository
    - removed: unnecessary public declaration of methods

CHANGED TableAction:
    - changed: 'setupCandiates' adds the top 5 (votes) of every category in possibleCandidates (Manual preselection necessary)
    - added: helper method 'getLimit
    - removed: 'addCandidate' and 'addPossibleCandidate' due to no usage

CHANGED voting.css:
    - added: font-size to 'h1', 'h2.categoryHeader' and 'button' to enhance readability

CHANGED addingCandidates.html:
    - changed: language used in html tag from 'en' to 'de'

ADDED alreadysubmittedcandidates.html:
    - added: error page if voter already submitted possibleCandidates
2020-12-07 23:42:35 +01:00
catoandGitHub 11b704a7e2 Update README.md 2020-12-07 23:25:27 +01:00
Casually9790 301ffc60f0 Fixed copying mechanism 2020-12-06 23:31:44 +01:00
Casually9790 7e39d2dab9 You know the drill 2020-12-06 01:19:39 +01:00
Casually9790 9531dadd69 Massive changes but I have to go to sleep now 2020-12-05 06:05:56 +01:00
Casually9790 56d3f9875c PossibleCandidates get saved to their own database now
CHANGED VotingController:
    - changed 'candidateSaving' possibleCandidates get Located by name and category and get saved now

CHANGED PossibleCandidateRepository:
    - removed 'findByNameAndCategoryID'
    - added 'findByNameAndCategory'

ADDED addingCandidates.css:
    - added basic styling

CHANGED voting.css:
    - bug that div gets cut off resolved

CHANGED addingCandidates.html:
    - changed display method from table to div

CHANGED: voting.html:
    - added div to style the site (bugfix)
2020-12-02 14:08:28 +01:00
Casually9790 0b138d647d Devided Candidates in PossibleCandidates and Candidates
CHANGED: VotingController:
    - added implementation of PossibleCandidateRepository 'possibleCandidateRepository'
    - 'VerifyName' added checking for candidatesubmit_status
    - 'VerifyName' changed Candidate to PossibleCandidate
    - 'VerifyName' changed CandidateWrapper to PossibleCandidateWrapper
    - added method 'candidateSaving'

ADDED: PossibleCandidate:
    - added entity PossibleCandidate

CHANGED: Voter
    - removed 'vote_status' from parameterized constructor
    - added candidatesubmit_status
    - added getter for candidatesubmit_status

CHANGED: Candidate
    - added 'Category category' to parameterized constructor

ADDED: PossibleCandidateWrapper
    - added method 'addPossibleCandidate'
    - added getters/setters

REMOVED: CandidateWrapper
ADDED: PossibleCandidateRepository:
    - added method 'findByNameAndCategoryID' (!!! Not working)
    - added method 'findById'

CHANGED: TableAction:
    - added needed elements to parameter lists because of changes in the entitys voter, candidate and possibleCandidate
    - added method 'logPossibleCandidates'

CHANGED: addingCandidates.html:
    - changed the pointer to list according to the changes in 'VotingController.VerifyName'
2020-12-02 14:07:58 +01:00
Casually9790 20df7cc38c Added candidate suggestion
- CHANGES: VotingControler:
    -- added: switch to toogle between candidate suggestion and voting
    -- added: suport for adding candidate names
    -- added: method to save given candidate names
- ADDED: addingCandidates:
    -- added: site to enter candidate suggestions
- ADDED: CandidateWrapper:
    -- added: complete class to help with data transferation through th:object
- CHANGES: Category:
    -- added: constructor with name parameter for logging purposes
- CHANGES: Candidate:
    -- added: setters for class variables
- CHANGES: TableAction:
    -- refactoring: moved method to vote and update vote status from VotingController to TableActions class
- CHANGES: Resources:
    -- added: Files with data to feed to the tables
- ADDED: voteSuccessful:
    -- added fallback html page
- REFACTORING: candidateAddingSuccessful:
    -- renamed file
2020-11-29 17:13:17 +01:00
Casually9790 910a110921 Change vote status of voter if voter voted
Through that voters can only vote once
2020-11-28 00:17:16 +01:00
Casually9790 16f947aaab Created a success.html page 2020-11-28 00:03:56 +01:00
Casually9790 38ee5d4178 Added the voting system 2020-11-28 00:03:36 +01:00
Casually9790 8b2d1f89f6 Dashboard gets displayed horizontally 2020-11-28 00:02:09 +01:00
Casually9790 423b307c66 Candidates get sorted descending by their votes within their categories 2020-11-27 23:59:14 +01:00
Casually9790 1b49a241cc Merge branch 'MailSystem' into main 2020-11-27 23:56:55 +01:00
Casually9790 c0e3ed1f3f Added basics for the mail system 2020-11-25 18:36:28 +01:00
Casually9790 71ac8d2feb Reset voting site to the basic 2020-11-25 18:35:05 +01:00
Casually9790 d265677b55 updated start site
start site has now user and admin login panels
added custom css for start page
dashboard is now splitted in cateogies
implemendet LiveReload
added fallback 'falseInput.html' page
2020-11-24 17:11:16 +01:00
Casually9790 d8db603ab6 Merge branch 'frontend' into main 2020-11-14 17:12:33 +01:00
Casually9790 51d464f0e0 Fixed damage done by false git usage 2020-11-14 17:08:10 +01:00
Casually9790 25484250d6 Updated the .gitignore file
Hopefully this will fix the including of all log files in the repo
2020-11-14 15:15:14 +01:00
Casually9790 72f1ff6c70 Merge branch 'backend' into main 2020-11-14 15:07:52 +01:00
Casually9790 a746110991 .gitignore expanded, Votingsite expanded
.gitignore:
    - All logs are now going to be ignored by git

VotingController.java:
    - added the categoryRepository to the model

voting.html:
     - showing data out of categoryRepository now
2020-11-14 13:03:43 +01:00
Casually9790 c8f5b32879 Skin changes
start.css:
    - added temporarily design

voting.css:
    - created the file
2020-11-14 01:18:21 +01:00
Casually9790 0887b54b23 Basic sytling für die Startseite erstellt
- Startseite arbeitet nun mit einem Flow-Design
- Weiterleitende Links angepasst
2020-11-14 01:18:21 +01:00
Casually9790 40655b2130 All fallbacks added to the login site. Edited voting.html
.gitignore:
    - added the /logs folder to the gitignore

VotingController.java:
    - added log message logging false entry format

Candidate.java:
    - return type changed from 'String' to 'Candidate'

wrongEmail.html:
    - added html file to avoid error

voting.html:
    - changed the way of output
2020-11-14 01:11:17 +01:00
Casually9790 ccc6190344 [FATAL ] SQL-Injektions werden verhindert
-Es werden  nur Eingaben des Typs '[a-z]+\\.[a- z]+@adolfinum+\\.de$' an die mySQL Tabelle weiter gebeben.
2020-11-12 22:54:10 +01:00
catoandGitHub 5e4608f40a Update README.md 2020-11-11 23:42:49 +01:00
catoandGitHub 18ba0d756a Update README.md 2020-11-11 23:42:25 +01:00
catoandGitHub 6598122c93 Update README.md 2020-11-11 23:42:02 +01:00
Casually9790 40d931ec0c Got a server set up to host the VotingSystem.
---------------------------------------------------
Internet address: http://***REMOVED***
Server IP address: ***REMOVED***
Port: 8000
--------------------------------------------------
Try to port the database to be hosted on the server rather
than on be hosted on localhost
2020-11-11 23:36:53 +01:00
Casually9790 8649301613 changed username of sql database 2020-11-10 23:30:43 +01:00
Casually9790 ab38911327 initial commit 2020-11-09 23:51:28 +01:00
44 changed files with 2128 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
Databases/
logs/
Results/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Logging ###
*.log
### DataPrivacy ###
Databases
logs
+117
View File
@@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
BIN
View File
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
+29
View File
@@ -1 +1,30 @@
# AbizeitungVotingSystem
This project is a custom web server constructed to simplify the voting process for our senior book <br>
All private data got removed <br>
You are free to use this web server <br>
Be aware that this webserver isn't really securly build. To avoid complications use a DNS mask
# Requierments
MySql
# Installation
Clone the repo <br>
Run `cd AbizeitungVotingSystem` <br>
Add your mysql database credentials and email credentials to the application.properties file <br>
Run `mvn install -DskipTests`<br>
The .jar is located in the target folder<br>
Run the .jar as a deamon on your machine <br>
Webserver is reachable at host_ip:8000 <br>
(Check if your firewall allows traffic over port 8000 if website is not reachable)
# Usage
Add your email whitelist to the file `AbizeitungVotingSystem/src/main/resources/Email_Whitelist.txt` <br>
Add your Categories to the file `AbizeitungVotingSystem/src/main/resources/Categories.txt` <br>
Adjust the regex in `VotingController.java` and `start.html` to suit your needs <br>
<br>
To alternate between adding and voting mode with the two coresponding booleans in the `VotingController.java` file
# Contribution
Feel free to fork the repo and submit a pull-request. I will review and add it happily :)
+95
View File
@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<groupId>com.github.cato447</groupId>
<artifactId>AbiVoting</artifactId>
<version>1.0</version>
<name>AbizeitungVotingSystem</name>
<description>A Voting System to get voting information for our graduating book</description>
<distributionManagement>
<repository>
<id>ssh-repository</id>
<url>scpexe://81.169.149.143/program</url>
</repository>
</distributionManagement>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<extensions>
<!-- Enabling the use of SSH -->
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh-external</artifactId>
<version>2.10</version>
</extension>
</extensions>
</build>
</project>
@@ -0,0 +1,17 @@
package com.github.cato447.AbizeitungVotingSystem;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import java.util.Arrays;
@SpringBootApplication
@EnableJpaRepositories
public class AbizeitungVotingSystemApplication {
public static void main(String[] args) {
SpringApplication.run(AbizeitungVotingSystemApplication.class, args);
}
}
@@ -0,0 +1,245 @@
package com.github.cato447.AbizeitungVotingSystem.controller;
import com.github.cato447.AbizeitungVotingSystem.entities.*;
import com.github.cato447.AbizeitungVotingSystem.helper.PossibleCandidateWrapper;
import com.github.cato447.AbizeitungVotingSystem.helper.RandomNumber;
import com.github.cato447.AbizeitungVotingSystem.repositories.*;
import com.github.cato447.AbizeitungVotingSystem.table.TableAction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.Month;
import java.util.*;
@Controller
public class VotingController {
private boolean votingPhase;
private boolean addingPhase;
private static final Logger LOGGER = LogManager.getLogger(VotingController.class);
private TableAction tableAction = new TableAction();
@Autowired
VoterRepository voterRepository;
@Autowired
CandidateRepository candidateRepository;
@Autowired
CategoryRepository categoryRepository;
@Autowired
PossibleCandidateRepository possibleCandidateRepository;
@Autowired
AuthCodesRepository authCodesRepository;
@Autowired
JavaMailSender emailSender;
@PostConstruct
public void init() {
votingPhase = true;
addingPhase = false;
LOGGER.info("Program started with arguments: votingPhase="+ votingPhase + " addingPhase=" + addingPhase);
if (voterRepository.findAll().size() == 0) {
tableAction.setUpVoters(voterRepository);
LOGGER.info("Voters successfully set up");
}
if (categoryRepository.findAll().size() == 0) {
tableAction.setUpCategories(categoryRepository);
possibleCandidateRepository.deleteAll();
LOGGER.info("Categories successfully set up");
}
if (candidateRepository.findAll().size() == 0 && votingPhase == true && possibleCandidateRepository.findAll().size() != 0) {
tableAction.setUpCandidates(possibleCandidateRepository, candidateRepository);
LOGGER.info("Candidates successfully set up");
}
}
@RequestMapping("/")
public String WelcomeSite() {
return "start.html";
}
public void sendSimpleMessage(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
emailSender.send(message);
}
@RequestMapping("/checkStatus")
public String VerifyName(@RequestParam String name, Model model) {
//Basic email regex
if (name.strip().toLowerCase().matches("[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+")) {
try {
LOGGER.warn(name);
Voter voter = voterRepository.findByEmail(name.toLowerCase().strip());
if (voter == null){
LOGGER.error(name + " is not allowed to vote");
return "errors/notRegistered.html";
}
if (voter.getVote_status() && votingPhase) {
LOGGER.warn(name + " has already voted");
return "errors/alreadyVoted.html";
} else if (voter.getCandidatesubmit_status() && addingPhase) {
LOGGER.warn(name + " has already submitted its candidates");
return "errors/alreadyVoted.html";
} else {
if (authCodesRepository.findByName(name) == null) {
AuthCode authCode = tableAction.generateToken(name, RandomNumber.getRandomNumberString(), authCodesRepository);
sendSimpleMessage(name, "Code zur Authentifizierung", "Dein Code lautet: " + authCode.getCode());
} else if (authCodesRepository.findByName(name) != null && authCodesRepository.findByName(name).isExpired()) {
AuthCode authCode = tableAction.generateToken(name, RandomNumber.getRandomNumberString(), authCodesRepository);
sendSimpleMessage(name, "Code zur Authentifizierung", "Dein Code lautet: " + authCode.getCode());
}
model.addAttribute("name", name);
model.addAttribute("codeTime", authCodesRepository.findByName(name).getExpirationTime());
model.addAttribute("codeExpired", false);
model.addAttribute("codeFalse", false);
return "authenticate.html";
}
} catch (Exception e) {
e.printStackTrace();
tableAction.deleteToken(name, authCodesRepository);
return "error.html";
}
} else {
return "errors/falseInput.html";
}
}
@RequestMapping("/newCode")
public String provideNewCode(@RequestParam String name, Model model){
AuthCode authCode = tableAction.generateToken(name, RandomNumber.getRandomNumberString(), authCodesRepository);
sendSimpleMessage(name, "Code zur Authentifizierung", "Dein Code lautet: " + authCode.getCode());
model.addAttribute("name", name);
model.addAttribute("codeTime", authCodesRepository.findByName(name).getExpirationTime());
model.addAttribute("codeExpired", false);
model.addAttribute("codeFalse", false);
return "authenticate.html";
}
@RequestMapping("/vote")
public String voting_adding(@RequestParam String code, @RequestParam String name, Model model) {
String tokenStatus = tableAction.checkToken(name, code, authCodesRepository);
if (tokenStatus.equals("matched")) {
LOGGER.warn("matched");
if (addingPhase) {
PossibleCandidateWrapper possibleCandidates = new PossibleCandidateWrapper();
List<Category> categories = categoryRepository.findAll();
for (int i = 0; i < categories.size(); i++) {
possibleCandidates.addPossibleCandidate(new PossibleCandidate());
}
model.addAttribute("categories", categories);
model.addAttribute("form", possibleCandidates);
model.addAttribute("name", name);
return "addingCandidates.html";
} else if (votingPhase) {
List<Category> categories = categoryRepository.findAll();
model.addAttribute("categories", categories);
model.addAttribute("name", name);
return "voting.html";
}
} else if (tokenStatus.equals("expired")) {
LOGGER.warn("expired");
model.addAttribute("name", name);
model.addAttribute("codeExpired", true);
model.addAttribute("codeFalse", false);
return "authenticate.html";
} else if (tokenStatus.equals("wrong")) {
LOGGER.warn("wrong");
model.addAttribute("name", name);
model.addAttribute("codeExpired", false);
model.addAttribute("codeFalse", true);
return "authenticate.html";
}
return "fatalError";
}
@RequestMapping("/saveCandidates")
public String candidateSaving(@ModelAttribute PossibleCandidateWrapper possibleCandidates, @RequestParam String name) {
if (voterRepository.findByEmail(name).getCandidatesubmit_status()) {
return "errors/alreadyVoted.html";
} else {
LinkedList<PossibleCandidate> posCandidates = possibleCandidates.getPossibleCandidates();
long index = 1;
for (PossibleCandidate posCandidate : posCandidates) {
if (posCandidate.getName() != "") {
if (possibleCandidateRepository.findByNameAndCategory(posCandidate.getName(), categoryRepository.findById(index).get()) != null) {
PossibleCandidate p = possibleCandidateRepository.findByNameAndCategory(posCandidate.getName(), categoryRepository.findById(index).get());
p.setVotes(p.getVotes() + 1);
possibleCandidateRepository.save(p);
} else if (possibleCandidateRepository.findByNameAndCategory(posCandidate.getName().split(" ")[posCandidate.getName().split(" ").length-1], categoryRepository.findById(index).get()) != null){
PossibleCandidate p = possibleCandidateRepository.findByNameAndCategory(posCandidate.getName().split(" ")[posCandidate.getName().split(" ").length-1], categoryRepository.findById(index).get());
p.setVotes(p.getVotes() + 1);
possibleCandidateRepository.save(p);
} else {
if (index > 31 && posCandidate.getName().indexOf(" ") != -1) {
if (posCandidate.getName().split(" ")[posCandidate.getName().split(" ").length-1].equals("Neumann") ||
posCandidate.getName().split(" ")[posCandidate.getName().split(" ").length-1].equals("neumann") ||
posCandidate.getName().split(" ")[posCandidate.getName().split(" ").length-1].equals("Mecklenburg") ||
posCandidate.getName().split(" ")[posCandidate.getName().split(" ").length-1].equals("mecklenburg")){
PossibleCandidate possibleCandidate = new PossibleCandidate(posCandidate.getName(), categoryRepository.findById(index).get());
possibleCandidateRepository.save(possibleCandidate);
} else {
posCandidate.setName(posCandidate.getName().split(" ")[posCandidate.getName().split(" ").length - 1]);
PossibleCandidate possibleCandidate = new PossibleCandidate(posCandidate.getName(), categoryRepository.findById(index).get());
possibleCandidateRepository.save(possibleCandidate);
}
} else {
PossibleCandidate possibleCandidate = new PossibleCandidate(posCandidate.getName(), categoryRepository.findById(index).get());
possibleCandidateRepository.save(possibleCandidate);
}
}
}
index++;
}
tableAction.updateCandidatesubmit_status(name, voterRepository);
return "voteSuccessful.html";
}
}
@RequestMapping("/processVote")
public String ProcessVote(@RequestParam String name, @RequestParam String voteValues) {
if (voterRepository.findByEmail(name).getVote_status()) {
return "errors/alreadyVoted.html";
} else {
LinkedList<String> voteFor = new LinkedList<>();
if(!voteValues.equals("")) {
String[] partVoteValues = voteValues.split(",");
for (String s : partVoteValues) {
voteFor.add(s);
}
LOGGER.info(name + " has voted!");
}
for (String s: voteFor) {
tableAction.voteForCandidate(s, candidateRepository);
}
tableAction.updateVotingStatus(name, voterRepository);
return "voteSuccessful.html";
}
}
}
@@ -0,0 +1,55 @@
package com.github.cato447.AbizeitungVotingSystem.entities;
import javax.persistence.*;
@Entity
@Table(name="auth_codes")
public class AuthCode {
public AuthCode(){
super();
}
public AuthCode(String name, String code) {
this.name = name;
this.code = code;
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String name;
private String code;
private long time = System.currentTimeMillis();
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public long getTime() {
return time;
}
public long getExpirationTime(){
return time + 1800*1000;
}
public boolean isExpired(){
return System.currentTimeMillis() >= (time + 1800*1000);
}
public void setTime(long time) {
this.time = time;
}
public Long getId() {
return id;
}
}
@@ -0,0 +1,69 @@
package com.github.cato447.AbizeitungVotingSystem.entities;
import javax.persistence.*;
@Entity
@Table(name = "candidates")
public class Candidate implements Comparable<Candidate>{
public Candidate() {
super();
}
public Candidate(String name, Category category) {
super();
this.name = name;
this.votes = 0;
this.category = category;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;
private Integer votes;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Integer getVotes() {
return votes;
}
public Category getCategory() {return category;}
public void setName(String name) {
this.name = name;
}
public void setVotes(Integer votes) {
this.votes = votes;
}
public void setCategory(Category category) {
this.category = category;
}
public void votedFor() {
this.votes += 1;
}
@Override
public int compareTo(Candidate c) {
if (getVotes() == null || c.getVotes() == null) {
return 0;
}
return c.getVotes().compareTo(getVotes());
}
}
@@ -0,0 +1,50 @@
package com.github.cato447.AbizeitungVotingSystem.entities;
import javax.persistence.*;
import java.util.Collections;
import java.util.List;
@Entity
@Table(name="categories")
public class Category {
public Category(){
super();
}
public Category(String name, List<Candidate> candidateList) {
super();
this.name = name;
this.candidateList = candidateList;
}
public Category(String name){
super();
this.name = name;
}
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private long id;
private String name;
@OneToMany(mappedBy = "category", fetch = FetchType.LAZY)
private List<Candidate> candidateList;
public long getId() {
return id;
}
public String getName() {
return name;
}
public List<Candidate> getCandidateList() {
Collections.sort(candidateList);
return candidateList;
}
public int getCandidateListSize(){
return candidateList.size();
}
}
@@ -0,0 +1,60 @@
package com.github.cato447.AbizeitungVotingSystem.entities;
import javax.persistence.*;
@Entity
@Table(name = "possibleCandidates")
public class PossibleCandidate{
public PossibleCandidate() {
super();
}
public PossibleCandidate(String name, Category category) {
super();
this.name = name;
this.category = category;
this.votes = 1;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int votes;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Category getCategory() {return category;}
public Long getCategoryID(){
return category.getId();
}
public Integer getVotes() {
return votes;
}
public void setName(String name) {
this.name = name;
}
public void setCategory(Category category) {
this.category = category;
}
public void setVotes(int votes) {
this.votes = votes;
}
}
@@ -0,0 +1,49 @@
package com.github.cato447.AbizeitungVotingSystem.entities;
import javax.persistence.*;
@Entity
@Table(name="voters")
public class Voter {
public Voter(){
super();
}
public Voter(String email) {
this.email = email;
this.vote_status = false;
this.candidatesubmit_status = false;
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String email;
private Boolean vote_status;
private Boolean candidatesubmit_status;
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
public Boolean getVote_status() {
return vote_status;
}
public Boolean getCandidatesubmit_status() {
return candidatesubmit_status;
}
public void vote(){
vote_status = true;
}
public void submitCandidates() {
candidatesubmit_status = true;
}
}
@@ -0,0 +1,27 @@
package com.github.cato447.AbizeitungVotingSystem.helper;
import com.github.cato447.AbizeitungVotingSystem.entities.PossibleCandidate;
import java.util.LinkedList;
public class PossibleCandidateWrapper {
private LinkedList<PossibleCandidate> possibleCandidates;
public PossibleCandidateWrapper(){
possibleCandidates = new LinkedList<>();
}
public void addPossibleCandidate(PossibleCandidate possibleCandidate){
this.possibleCandidates.add(possibleCandidate);
}
public LinkedList<PossibleCandidate> getPossibleCandidates() {
return possibleCandidates;
}
public void setPossibleCandidates(LinkedList<PossibleCandidate> possibleCandidates) {
this.possibleCandidates = possibleCandidates;
}
}
@@ -0,0 +1,15 @@
package com.github.cato447.AbizeitungVotingSystem.helper;
import java.util.Random;
public class RandomNumber {
public static String getRandomNumberString() {
// It will generate 6 digit random Number.
// from 0 to 999999
Random rnd = new Random();
int number = rnd.nextInt(999999);
// this will convert any number sequence into 6 character.
return String.format("%06d", number);
}
}
@@ -0,0 +1,12 @@
package com.github.cato447.AbizeitungVotingSystem.repositories;
import com.github.cato447.AbizeitungVotingSystem.entities.AuthCode;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
public interface AuthCodesRepository extends JpaRepository<AuthCode, Integer> {
public AuthCode findByName(String name);
}
@@ -0,0 +1,17 @@
package com.github.cato447.AbizeitungVotingSystem.repositories;
import com.github.cato447.AbizeitungVotingSystem.entities.Candidate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface CandidateRepository extends JpaRepository<Candidate, Integer> {
public Candidate findByName(String name);
Optional<Candidate> findById(Long id);
}
@@ -0,0 +1,12 @@
package com.github.cato447.AbizeitungVotingSystem.repositories;
import com.github.cato447.AbizeitungVotingSystem.entities.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface CategoryRepository extends JpaRepository<Category, Long> {
public Category findByName(String name);
}
@@ -0,0 +1,16 @@
package com.github.cato447.AbizeitungVotingSystem.repositories;
import com.github.cato447.AbizeitungVotingSystem.entities.Category;
import com.github.cato447.AbizeitungVotingSystem.entities.PossibleCandidate;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface PossibleCandidateRepository extends JpaRepository<PossibleCandidate, Integer> {
PossibleCandidate findByNameAndCategory(String name, Category category);
Optional<PossibleCandidate> findById(Long id);
}
@@ -0,0 +1,14 @@
package com.github.cato447.AbizeitungVotingSystem.repositories;
import com.github.cato447.AbizeitungVotingSystem.entities.Voter;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface VoterRepository extends JpaRepository<Voter, Integer> {
public Voter findByEmail(String email);
public Voter findById(Long id);
}
@@ -0,0 +1,139 @@
package com.github.cato447.AbizeitungVotingSystem.table;
import com.github.cato447.AbizeitungVotingSystem.entities.*;
import com.github.cato447.AbizeitungVotingSystem.repositories.*;
import java.io.*;
import java.util.*;
public class TableAction {
public TableAction(){
}
public void updateVotingStatus(String email, VoterRepository voterRepository){
Voter voter = voterRepository.findByEmail(email);
voter.vote();
voterRepository.save(voter);
}
public void updateCandidatesubmit_status(String email, VoterRepository voterRepository){
Voter voter = voterRepository.findByEmail(email);
voter.submitCandidates();
voterRepository.save(voter);
}
public AuthCode generateToken(String name, String code, AuthCodesRepository authCodesRepository) {
AuthCode authCode = new AuthCode(name, code);
try{
AuthCode existingCode = authCodesRepository.findByName(authCode.getName());
existingCode.setCode(code);
existingCode.setTime(System.currentTimeMillis());
authCodesRepository.save(existingCode);
return existingCode;
} catch (Exception e){
authCodesRepository.save(authCode);
return authCode;
}
}
public String checkToken(String name, String code, AuthCodesRepository authCodesRepository){
try {
AuthCode authCode = authCodesRepository.findByName(name);
if (authCode.getCode().equals(code) && !authCode.isExpired()) {
authCodesRepository.delete(authCode);
return "matched";
} else if (authCode.isExpired()) {
authCodesRepository.delete(authCode);
return "expired";
}
} catch(Exception e){
return "wrong";
}
return "wrong";
}
public void deleteToken(String name, AuthCodesRepository authCodesRepository){
authCodesRepository.delete(authCodesRepository.findByName(name));
}
private int getLimit(List<PossibleCandidate> possibleCandidates){
return possibleCandidates.size() <= 15 ? possibleCandidates.size() : 15;
}
public void voteForCandidate(String id, CandidateRepository candidateRepository){
long candidateID = Long.valueOf(id);
Candidate candidate = candidateRepository.findById(candidateID).get();
candidate.votedFor();
candidateRepository.save(candidate);
}
public void setUpVoters(VoterRepository voterRepository){
try (InputStream inputStream = getClass().getResourceAsStream("/Email_Whitelist.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line = "";
ArrayList<Voter> voters = new ArrayList<Voter>();
while ((line = reader.readLine())!= null){
String email = line;
Voter voter = new Voter(email);
voters.add(voter);
}
voterRepository.saveAll(voters);
} catch (IOException e) {
e.printStackTrace();
}
}
public void setUpCandidates(PossibleCandidateRepository possibleCandidateRepository, CandidateRepository candidateRepository){
List<PossibleCandidate> possibleCandidates = possibleCandidateRepository.findAll();
Collections.sort(possibleCandidates, Comparator.comparing(PossibleCandidate::getCategoryID));
long category_id = possibleCandidates.get(0).getCategory().getId();
List<PossibleCandidate> possibleCandidatesPerCategory = new LinkedList<>();
for (int i = 0; i < possibleCandidates.size(); i++) {
PossibleCandidate p = possibleCandidates.get(i);
if (category_id == p.getCategory().getId()){
possibleCandidatesPerCategory.add(p);
} else {
category_id = p.getCategory().getId();
Collections.sort(possibleCandidatesPerCategory, Comparator.comparing(PossibleCandidate::getVotes));
Collections.reverse(possibleCandidatesPerCategory);
for (int j = 0; j < getLimit(possibleCandidatesPerCategory); j++){
if (j >= 10 && possibleCandidatesPerCategory.get(j).getVotes() == possibleCandidatesPerCategory.get(9).getVotes()){
Candidate candidate = new Candidate(possibleCandidatesPerCategory.get(j).getName(), possibleCandidatesPerCategory.get(j).getCategory());
candidateRepository.save(candidate);
}
if (j < 10){
Candidate candidate = new Candidate(possibleCandidatesPerCategory.get(j).getName(), possibleCandidatesPerCategory.get(j).getCategory());
candidateRepository.save(candidate);
}
if (j == 14){
break;
}
}
i += -1;
possibleCandidatesPerCategory.clear();
}
}
for (int j = 0; j < getLimit(possibleCandidatesPerCategory); j++){
Candidate candidate = new Candidate(possibleCandidatesPerCategory.get(j).getName(), possibleCandidatesPerCategory.get(j).getCategory());
candidateRepository.save(candidate);
}
}
public void setUpCategories(CategoryRepository categoryRepository){
try (InputStream inputStream = getClass().getResourceAsStream("/Categories.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line = "";
ArrayList<Category> categories = new ArrayList<Category>();
while ((line = reader.readLine())!= null){
String name = line;
Category category = new Category(name);
categories.add(category);
}
categoryRepository.saveAll(categories);
} catch (IOException e) {
e.printStackTrace();
}
}
}
View File
+20
View File
@@ -0,0 +1,20 @@
spring.datasource.url=jdbc:mysql://hostadress/database
spring.datasource.username=mysql_username
spring.datasource.password=mysql_password
spring.jpa.hibernate.ddl-auto=update
# Tomcat
server.port = 8000
######Email Properties ######
spring.mail.host=
spring.mail.port=
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.username=
spring.mail.password=
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=5000
spring.mail.properties.mail.smtp.writetimeout=5000
+41
View File
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOGS" value="./logs" />
<appender name="Console"
class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
</Pattern>
</layout>
</appender>
<appender name="RollingFile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOGS}/Voting.log</file>
<encoder
class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d %p [%t] %m%n</Pattern>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily and when the file reaches 10 MegaBytes -->
<fileNamePattern>${LOGS}/archived/%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<!-- LOG everything at INFO level -->
<root level="info">
<appender-ref ref="RollingFile" />
<appender-ref ref="Console" />
</root>
</configuration>
@@ -0,0 +1,66 @@
body {
background-color: rgb(44, 49, 54);
font-family: Arial, Helvetica, sans-serif;
}
.center-screen {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.centered {
background: transparent;
margin: 0 auto;
padding: 20px;
width: 100%;
overflow: auto;
}
h1 {
color: #FFF;
}
h2 {
color: #FFF;
}
h2.categoryCategory {
color: #bb1515;
margin-top: 5%;
margin-bottom: 1%;
}
input {
background: #bb1515b2;
border: none;
color: #FFF;
font-size: .875rem;
font-weight: normal;
text-align: left;
transition: opacity .25s .5s;
}
.submitButton {
margin-top: 5%;
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
html,
body {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom right, #111E25 0%, #111 100%);
font-family: 'Lato', sans-serif;
}
@@ -0,0 +1,51 @@
body {
background-color: rgb(44, 49, 54);
font-family: Arial, Helvetica, sans-serif;
}
.center-screen {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.centered {
background: transparent;
margin: 0 auto;
padding: 20px;
width: 100%;
overflow: auto;
}
h1 {
color: #FFF;
font-size: 75px;
}
h2 {
color: #6d6d78;
}
.submitButton {
padding: .25em 0;
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
html,
body {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom right, #111E25 0%, #111 100%);
font-family: 'Lato', sans-serif;
}
@@ -0,0 +1,84 @@
body {
background-color: rgb(44, 49, 54);
font-family: Arial, Helvetica, sans-serif;
}
.center-screen {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.centered {
background: transparent;
margin: 0 auto;
padding: 20px;
width: 100%;
overflow: auto;
transform: scale();
}
h1 {
color: #FFF;
}
h1.end {
color: #6d6d78;
margin-bottom: 2%;
}
h2 {
color: #FFF;
}
input {
background-color: transparent;
border: transparent;
border-bottom: 2px solid red;
color: #FFF;
font-size: xx-large;
width: 1ch;
}
.submitButton {
margin-top: 5%;
margin-bottom: 1%;
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
.newCode {
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 1rem;
width: 250px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
.errorCode {
margin-top: 10%;
color: #bb1515;
}
html,
body {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom right, #111E25 0%, #111 100%);
font-family: 'Lato', sans-serif;
}
@@ -0,0 +1,66 @@
body {
background-color: rgb(44, 49, 54);
font-family: Arial, Helvetica, sans-serif;
}
.center-screen {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.centered {
background: transparent;
margin: 0 auto;
padding: 20px;
width: 100%;
overflow: auto;
}
h1 {
color: #FFF;
font-size: 75px;
}
h2.mottoHeader {
color: #FFF;
font-size: 50px;
}
button {
background: transparent;
border: none;
color: #FFF;
font-size: 35px;
font-weight: normal;
letter-spacing: .125rem;
text-transform: uppercase;
text-align: center;
transition: opacity .25s .5s;
margin: 0.5%;
}
.submitButton {
margin-top: 5%;
padding: .25em 0;
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
html,
body {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom right, #111E25 0%, #111 100%);
font-family: 'Lato', sans-serif;
}
@@ -0,0 +1,53 @@
html,
body {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom right, #111E25 0%, #111 100%);
font-family: 'Lato', sans-serif;
}
div.userLogin {
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
#signup_button {
display: block;
margin: auto;
padding: .25em 0;
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
input.email_input {
background-color: transparent;
border: transparent;
border-bottom: 2px solid #bb1515;
color: #FFF;
font-size: xx-large;
width: 500px;
}
input.password_input {
background-color: transparent;
border: transparent;
color: #FFF;
font-size: medium;
width: fit-content;
}
label {
color: #FFF;
display: block;
}
@@ -0,0 +1,54 @@
body {
background-color: rgb(44, 49, 54);
font-family: Arial, Helvetica, sans-serif;
}
.center-screen {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.centered {
background: transparent;
margin: 0 auto;
padding: 20px;
width: 100%;
overflow: auto;
}
h1 {
color: #FFF;
}
h2 {
color: #FFF;
}
h2.time_remain {
color: #6d6d78;
}
.submitButton {
padding: .25em 0;
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
html,
body {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom right, #111E25 0%, #111 100%);
font-family: 'Lato', sans-serif;
}
@@ -0,0 +1,83 @@
body {
background-color: rgb(44, 49, 54);
font-family: Arial, Helvetica, sans-serif;
}
.center-screen {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.centered {
background: transparent;
margin: 0 auto;
padding: 20px;
width: 100%;
overflow: auto;
}
h1 {
color: #FFF;
font-size: 75px;
}
h2.categoryHeader {
color: #FFF;
font-size: 50px;
}
button {
background: transparent;
border: none;
color: #FFF;
font-size: 35px;
font-weight: normal;
letter-spacing: .125rem;
text-transform: uppercase;
text-align: center;
transition: opacity .25s .5s;
}
.submitButton {
margin-top: 2%;
margin-bottom: 5%;
padding: .25em 0;
border: 0;
outline: 0;
background: #bb1515;
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
@media only screen and (max-device-width: 480px) {
.submitButton {
margin-top: 2%;
padding: .25em 0;
border: 0;
outline: 0;
background: rgb(6, 216, 136);
color: rgba(255, 255, 255, 0.85);
font-size: 2rem;
width: 500px;
letter-spacing: .0625rem;
border-radius: 12px;
box-shadow: 0 3px 5px 1px rgba(0, 0, 0, 0.25);
text-shadow: 0 -2px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.2);
}
}
html,
body {
width: 100%;
height: 100%;
background-image: linear-gradient(to bottom right, #111E25 0%, #111 100%);
font-family: 'Lato', sans-serif;
}
@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/addingCandidates.css}" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body class="center-screen">
<script>
var q = 1,
qMax = 0;
$(function() {
qMax = $('#inputForm div.group').length;
$('#inputForm div.group').hide();
$('#inputForm div.group:nth-child(1)').show();
$('#btnNext').on('click', function(event) {
event.preventDefault();
handleClick();
});
});
function handleClick() {
if (q < qMax) {
$('#inputForm div.group:nth-child(' + q + ')').hide();
$('#inputForm div.group:nth-child(' + (q + 1) + ')').show();
if (q == (qMax - 1)) {
$('#btnNext').html('Eingabe bestätigen');
}
q++;
} else {
document.getElementById("inputForm").submit();
}
}
</script>
<div class="centered">
<h1>Schlage für jede Kategorie eine/n Kandiadt/en vor:</h1>
<form id="inputForm" action="#" th:action="@{/saveCandidates}" th:object="${form}" method="post">
<div id="candidateAdding">
<div class="group" th:each="category, itemStat : ${categories}">
<div th:if="${itemStat.index > 30}">
<h2 class="categoryCategory">Lehrer</h2>
</div>
<div th:unless="${itemStat.index > 30}">
<h2 class="categoryCategory">Schüler</h2>
</div>
<h2 class="categoryHeader" th:text="${category.name}"></h2>
<input th:field="*{possibleCandidates[__${itemStat.index}__].name}" />
</div>
</div>
<button class="submitButton" id="btnNext" type="submit">Nächste Kategorie</button>
<input id="voterName" type="hidden" name="name" th:value="${name}" />
</form>
</div>
</body>
</html>
@@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link th:href="@{/styles/authenticate.css}" rel="stylesheet" />
</head>
<body class="center-screen">
<h1 th:text="|Dir wurde ein Code an ${name} gesendet!|"></h1>
<h1>Nicht wundern das kann ein wenig dauern!</h1>
<h1 id="time_remain" class="end" th:text="${codeTime}"></h1>
<h2>Bitte gebe den Authentifizierungscode ein</h2>
<form action="#" id="passForm" th:action="@{/vote}" method="post">
<input id="voterName" type="hidden" name="name" th:value="${name}" />
<input id="authCode" type="hidden" name="code" value="" />
<input type="text" placeholder="•" class="passInput" name="pass[]" maxlength="1" autocomplete="off" required pattern="\d{1}" autofocus>
<input type="text" placeholder="•" class="passInput" name="pass[]" maxlength="1" autocomplete="off" required pattern="\d{1}">
<input type="text" placeholder="•" class="passInput" name="pass[]" maxlength="1" autocomplete="off" required pattern="\d{1}">
<input type="text" placeholder="•" class="passInput" name="pass[]" maxlength="1" autocomplete="off" required pattern="\d{1}">
<input type="text" placeholder="•" class="passInput" name="pass[]" maxlength="1" autocomplete="off" required pattern="\d{1}">
<input type="text" placeholder="•" class="passInput" name="pass[]" maxlength="1" autocomplete="off" required pattern="\d{1}">
</form>
<button class="submitButton" id="signup_button" onclick="getCode()">Abstimmen</button>
<form action="#" th:action="@{/newCode}" method="post">
<input id="voterName" type="hidden" name="name" th:value="${name}" />
<button class="newCode">Neuen Code anfordern</button>
</form>
<div th:if="${codeExpired}">
<h2 class="errorCode">Der Code ist nicht mehr gültig!</h2>
</div>
<div th:if="${codeFalse}">
<h2 class="errorCode">Der eingegebene Code ist falsch</h2>
</div>
<script>
const $inp = $(".passInput");
$inp.on({
paste(ev) { // Handle Pasting
const clip = ev.originalEvent.clipboardData.getData('text').trim();
// Allow numbers only
if (!/\d{6}/.test(clip)) return ev.preventDefault(); // Invalid. Exit here
// Split string to Array or characters
const s = [...clip];
// Populate inputs. Focus last input.
$inp.val(i => s[i]).eq(5).focus();
},
input(ev) { // Handle typing
const i = $inp.index(this);
if (this.value) $inp.eq(i + 1).focus();
},
keydown(ev) { // Handle Deleting
const i = $inp.index(this);
if (!this.value && ev.key === "Backspace" && i) $inp.eq(i - 1).focus();
}
});
function getCode() {
groupInputs = document.querySelectorAll(".passInput");
var code = "";
groupInputs.forEach(input => {
console.log("Input: " + input.value);
code += input.value;
});
console.log("Code: " + code);
output = document.getElementById("authCode");
output.value = code;
document.getElementById("passForm").submit();
}
function updateCounter(time) {
const zeroPad = (num, places) => String(num).padStart(places, '0')
// Show time remaining now.
showTimeRemaining();
var dateFuture = time;
console.log(dateFuture);
// Set a timer to update the displayed clock every 1000 milliseconds.
var updateTimeLoop = setInterval(showTimeRemaining, 1000);
function showTimeRemaining() {
var dateNow = Date.now();
var days = zeroPad(Math.floor((dateFuture - dateNow) / (1000 * 60 * 60 * 24)), 2);
var hours = zeroPad(Math.floor(((dateFuture - dateNow) - days * 1000 * 60 * 60 * 24) / (1000 * 60 * 60)), 2);
var mins = zeroPad(Math.floor(((dateFuture - dateNow) - days * 1000 * 60 * 60 * 24 - hours * 1000 * 60 * 60) / (1000 * 60)), 2);
var secs = zeroPad(Math.floor(((dateFuture - dateNow) - days * 1000 * 60 * 60 * 24 - hours * 1000 * 60 * 60 - mins * 1000 * 60) / 1000), 2);
document.getElementById("time_remain").innerHTML = "Der Code gilt noch für " + mins + ":" + secs;
if (hours + mins + secs == 0) {
document.getElementById("time_remain").innerHTML = "Der Code ist abgelaufen";
}
}
}
updateCounter(document.getElementById("time_remain").innerHTML);
</script>
</body>
</html>
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
+24
View File
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/allreadyVoted.css}" rel="stylesheet" />
</head>
<body class="center-screen">
<div class="centered">
<h1>Oops irgendwas ist kaputt gegangen</h1>
<h2 id="time_remain">Keine Sorge! <br> Probiers nochmal :D</h2>
<button id="backButton" class="submitButton">Zurück zum Anfang</button>
</div>
<script>
document.getElementById("backButton").onclick = function() {
location.href = "/";
};
</script>
</body>
</html>
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/allreadyVoted.css}" rel="stylesheet" />
</head>
<body class="center-screen">
<div class="centered">
<h1>Du hast schon abgestimmt!</h1>
<button id="backButton" class="submitButton">Zurück zum Anfang</button>
</div>
<script>
document.getElementById("backButton").onclick = function() {
location.href = "/";
};
</script>
</body>
</html>
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Abizeitung 2020/2021 Voting</title>
</head>
<body>
<h1 style="color: red;"> Der eingegebene Name entspricht nicht den Vorgaben </h1>
<a href="/">Return to Login-Site</a>
</body>
</html>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/allreadyVoted.css}" rel="stylesheet" />
</head>
<body class="center-screen">
<div class="centered">
<h1>Du bist nicht zur Wahl zugelassen!</h1>
<h2 id="time_remain">Falls du abstimmen können solltest <br> schreibe eine Mail an ***REMOVED***</h2>
<button id="backButton" class="submitButton">Zurück zum Anfang</button>
</div>
<script>
document.getElementById("backButton").onclick = function() {
location.href = "/";
};
</script>
</body>
</html>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/allreadyVoted.css}" rel="stylesheet" />
</head>
<body class="center-screen">
<div class="centered">
<h1>Du bist leider zu spät</h1>
<h2>Momentan ist hier auf der Seite nichts los</h2>
<button id="backButton" class="submitButton">Zur Adolfinum Seite</button>
</div>
<script>
document.getElementById("backButton").onclick = function() {
location.href = "https://adolfinum.de";
};
</script>
</body>
</html>
@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/mottoVoting.css}" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body class="center-screen">
<div class="centered">
<h1>Wähle das Abimotto:</h1>
<div id="votingButtons">
<div th:each="motto : ${mottos}" class="voteDiv">
<button class="inputButton" th:id="|motto${motto.id}|" th:text="${motto.name}" th:onclick="|setColor(motto${motto.id})|"></button>
</div>
</div>
<form action="#" th:action="@{/saveMotto}" method="post" id="mottoForm">
<input id="voteValue" type="hidden" name="voteValue" value="" />
<input id="voterName" type="hidden" name="name" th:value="${name}" />
<button class="submitButton" id="btnNext" onclick=getVotes()>Auswahl bestätigen</button>
</form>
</div>
<script>
function setColor(button) {
console.log(button);
groupButtons = document.querySelectorAll('[id^=motto]');
groupButtons.forEach(button => {
button.style.background = 'transparent';
button.style.fontWeight = 'normal';
});
button.style.background = "#bb1515";
button.style.fontWeight = 'bold';
}
function rgbToHex(rgb) {
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
var color = (rgb && rgb.length === 4) ? "#" +
("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) +
("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) +
("0" + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';
return color;
}
function getVotes() {
groupButtons = document.querySelectorAll(".inputButton");
var voteID;
groupButtons.forEach(button => {
if (button.style.fontWeight == 'bold') {
voteID = 5;
}
});
input = document.getElementById("voteValue");
input.value = voteID;
document.getElementById("mottoForm").submit();
}
</script>
</body>
</html>
+40
View File
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Abizeitung 2020/2021 Voting</title>
<link th:href="@{/styles/start.css}" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<script>
window.addEventListener("load", function() {
document.getElementById("signup_button").addEventListener("click", test);
});
function test() {
let input_field = document.getElementById("email_input");
let input = input_field.value;
if (!input.trim().match("[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+")) {
input_field.value = "";
alert("Die Email-Adresse \"" + input + "\" entspricht nicht den Vorgaben");
}
}
</script>
<div class="userLogin">
<form action="#" th:action="@{/checkStatus}" method="post">
<label for="email_input">
<span class="label-text">Adolfinum E-Mail</span>
</label>
<input class="email_input" id="email_input" type="text" placeholder="name.nachname@adolfinum.de" name="name" required autofocus />
<p></p>
<button type="submit" id="signup_button">Abstimmen</button>
</form>
</div>
</body>
</html>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/voteSuccessful.css}" rel="stylesheet" />
</head>
<body class="center-screen">
<div class="centered">
<h1>Deine Auswahl fließt nun in die Wahl ein!</h1>
<h2>Danke fürs abstimmen!</h2>
<button id="backButton" class="submitButton">Zurück zum Anfang</button>
</div>
</body>
<script>
document.getElementById("backButton").onclick = function() {
location.href = "/";
};
</script>
</html>
+92
View File
@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link th:href="@{/styles/voting.css}" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body class="center-screen">
<script>
function setColor(button) {
groupButtons = document.querySelectorAll('[id^=' + button.id.split("_")[0] + ']');
groupButtons.forEach(button => {
button.style.background = 'transparent';
button.style.fontWeight = 'normal';
});
button.style.background = "#bb1515";
button.style.fontWeight = 'bold';
}
function rgbToHex(rgb) {
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
var color = (rgb && rgb.length === 4) ? "#" +
("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) +
("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) +
("0" + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';
return color;
}
function getVotes() {
groupButtons = document.querySelectorAll(".inputButton");
var voteIds = [];
groupButtons.forEach(button => {
if (button.style.fontWeight == 'bold') {
var str = button.id.split("_");
voteIds.push(str[1].replace(/\D/g, ""));
}
});
input = document.getElementById("voteValues");
input.value = voteIds;
}
var q = 1,
qMax = 0;
$(function() {
qMax = $('#votingButtons div.voteDiv').length;
$('#votingButtons div.voteDiv').hide();
$('#votingButtons div.voteDiv:nth-child(1)').show();
$('#btnNext').on('click', function(event) {
event.preventDefault();
handleClick();
});
});
function handleClick() {
if (q < qMax) {
$('#votingButtons div.voteDiv:nth-child(' + q + ')').hide();
$('#votingButtons div.voteDiv:nth-child(' + (q + 1) + ')').show();
if (q == (qMax - 1)) {
$('#btnNext').html('Auswahl bestätigen');
}
q++;
} else {
getVotes();
document.getElementById("myForm").submit();
}
}
</script>
<div class="centered">
<h1>Wähle deine Kandidaten:</h1>
<div id="votingButtons">
<div th:each="category,iter : ${categories}" class="voteDiv">
<h2 class="categoryHeader" th:text="${category.name}"></h2>
<div th:each="candidate : ${category.candidateList}">
<button class="inputButton" th:id="|category${category.id}_candidate${candidate.id}|" th:text="${candidate.name}" th:onclick="|setColor(category${category.id}_candidate${candidate.id})|"></button>
</div>
</div>
</div>
<button class="submitButton" id="btnNext" type="submit">Nächste Kategorie</button>
<form action="#" th:action="@{/processVote}" method="post" id="myForm">
<input id="voteValues" type="hidden" name="voteValues" value="" />
<input id="voterName" type="hidden" name="name" th:value="${name}" />
</form>
</div>
</body>
</html>
@@ -0,0 +1,13 @@
package com.github.cato447.AbizeitungVotingSystem;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AbizeitungVotingSystemApplicationTests {
@Test
void contextLoads() {
}
}