Compare commits

...
98 Commits
Author SHA1 Message Date
Casually9790 9b4bfec933 Changed regex to basic email regex 2021-02-13 17:49:50 +01:00
catoandGitHub 5b3b5b35e5 Update README.md 2021-02-13 17:49:20 +01:00
catoandGitHub a49725cdd6 Delete logs directory 2021-02-13 17:37:30 +01:00
catoandGitHub 921ad8fde3 Delete Databases directory 2021-02-13 17:37:20 +01:00
Casually9790 292b71bd2d Privacy changes 2021-02-13 17:37:04 +01:00
catoandGitHub 4371468bbd Update README.md 2021-02-13 17:34:13 +01:00
catoandGitHub 2491657be2 Update README.md 2021-01-27 19:22:18 +01:00
Casually9790 9e1aaaea7b Merge branch 'testing' into main 2021-01-27 18:28:47 +01:00
Casually9790 ddb84f26f1 Merge remote-tracking branch 'origin/testing' into testing 2021-01-27 18:26:40 +01:00
Casually9790 bd288d4d1c Cleaning up for publication 2021-01-27 18:26:35 +01:00
catoandGitHub e44e5f80c6 Update README.md 2021-01-27 18:26:05 +01:00
catoandGitHub 48441e1924 Update README.md 2021-01-27 18:21:29 +01:00
Casually9790 179af51456 Updated Datelock 2021-01-17 16:19:26 +01:00
Casually9790 ddf670cc94 Prepared everything for the votings 2021-01-17 15:54:32 +01:00
Casually9790 ab3a36859a asdf3 2021-01-09 22:09:48 +01:00
Casually9790 4ec09ac16c asdf2 2021-01-09 22:08:55 +01:00
Casually9790 4e24723358 asdf 2021-01-09 22:07:45 +01:00
Casually9790 3528c1d0d3 Final release 2021-01-09 21:56:17 +01:00
Casually9790 69b320b28b Added exeptions 2021-01-09 21:26:13 +01:00
Casually9790 7fd2221e0a reverse block 2021-01-09 21:09:46 +01:00
Casually9790 a14d2a6f33 temp 2021-01-09 20:34:55 +01:00
Casually9790 577015fa3f BUGFIX 2021-01-09 20:23:16 +01:00
Casually9790 b5d18c63ce Fixed out of bounds exception 2021-01-09 18:32:31 +01:00
Casually9790 4c56f2c0ab Back up of first Database 2021-01-09 03:50:07 +01:00
Casually9790 436142f546 Added second voting 2021-01-09 02:35:55 +01:00
Casually9790 e9e9cfca97 Made final changes 2021-01-04 02:22:00 +01:00
Casually9790 dfaf2c55e2 Expanded the number of candidates from 5 to 10-15 2021-01-04 00:42:43 +01:00
Casually9790 f63847a092 Updated Requierments 2020-12-29 19:23:45 +01:00
Casually9790 907db5fb36 QOL 2020-12-27 23:33:56 +01:00
Casually9790 2a8ba84bec Added a datelock to end votes automatically 2020-12-27 23:23:41 +01:00
Casually9790 0597edf116 Changed to Voting Mode 2020-12-27 22:37:20 +01:00
Casually9790 4005fd4edb Google account go taken down switched it 2020-12-25 22:20:26 +01:00
Casually9790 d56514a712 Added names to the voter list 2020-12-18 21:04:02 +01:00
Casually9790 63f4e7c907 Fix error message 2020-12-18 20:11:28 +01:00
Casually9790 47beddf9c8 Final 2020-12-18 20:01:54 +01:00
Casually9790 d7b6283ffe End 2020-12-18 19:25:40 +01:00
Casually9790 fb7b0271b3 Removed code pass through 2020-12-18 19:04:12 +01:00
Casually9790 f65ff26e48 Finishing touches 2020-12-18 18:57:29 +01:00
Casually9790 d0f62b4ca6 Must function now Email is down 2020-12-17 03:11:28 +01:00
Casually9790 221ebf1c2b fixed bug 2020-12-16 23:49:50 +01:00
Casually9790 5cfade336e test 2020-12-16 23:49:27 +01:00
Casually9790 80b20f2098 Test 2020-12-16 20:03:14 +01:00
Casually9790 a37a1e1a91 Changed categories 2020-12-13 21:29:33 +01:00
Casually9790 6d83a513f1 Make input field static 2020-12-13 17:25:27 +01:00
Casually9790 3b0d0ec982 Fixed email input bug 2020-12-13 17:20:42 +01:00
Casually9790 8946853604 Css changes to start site 2020-12-13 17:18:56 +01:00
Casually9790 03e5e1b455 Removed the dashboard because of the existence of phpmyadmin 2020-12-12 00:02:58 +01:00
Casually9790 b32cc63e76 Regex ignores whitespaces now and added all fallback pages 2020-12-11 23:53:42 +01:00
Casually9790 3cd8670cef Merge remote-tracking branch 'origin/main' into main 2020-12-11 21:21:35 +01:00
Casually9790 efc2bd7501 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 5202042894 added 2020-12-11 00:20:17 +01:00
Casually9790 512331d3ab added 2020-12-11 00:03:40 +01:00
Casually9790 4dd3468bef Have to go to bed now need to write 2020-12-10 23:50:44 +01:00
Casually9790 7a64b195ed 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 5204322f40 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 9bf9f8acdc Added dynamic resource parsing and fixed breaking bug 2020-12-08 23:03:14 +01:00
Casually9790 525ba69689 Merge remote-tracking branch 'origin/main' into main 2020-12-07 23:42:40 +01:00
Casually9790 1ee5174207 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 3effc5364e Update README.md 2020-12-07 23:25:27 +01:00
Casually9790 32894e5469 Fixed copying mechanism 2020-12-06 23:31:44 +01:00
Casually9790 75ccf7d28e You know the drill 2020-12-06 01:19:39 +01:00
Casually9790 a642ea9890 Massive changes but I have to go to sleep now 2020-12-05 06:05:56 +01:00
Casually9790 525074af15 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 361433bf22 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 6a6ef2b96d 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 81befda3a9 Change vote status of voter if voter voted
Through that voters can only vote once
2020-11-28 00:17:16 +01:00
Casually9790 7b4f460445 Created a success.html page 2020-11-28 00:03:56 +01:00
Casually9790 75ad93bde0 Added the voting system 2020-11-28 00:03:36 +01:00
Casually9790 23e4a27d38 Dashboard gets displayed horizontally 2020-11-28 00:02:09 +01:00
Casually9790 ae1ba8b9c5 Candidates get sorted descending by their votes within their categories 2020-11-27 23:59:14 +01:00
Casually9790 780448537b Merge branch 'MailSystem' into main 2020-11-27 23:56:55 +01:00
Casually9790 7690d05fd2 Added basics for the mail system 2020-11-25 18:36:28 +01:00
Casually9790 b52d4b47b9 Reset voting site to the basic 2020-11-25 18:35:05 +01:00
Casually9790 6dfe2a3329 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 27f80636f0 Merge branch 'frontend' into main 2020-11-14 17:12:33 +01:00
Casually9790 264bfa8bf0 Fixed damage done by false git usage 2020-11-14 17:08:10 +01:00
Casually9790 ad91dbd5e9 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 c608a9d207 Merge branch 'backend' into main 2020-11-14 15:07:52 +01:00
Casually9790 e1d8d81095 .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 7713b1d3ec Skin changes
start.css:
    - added temporarily design

voting.css:
    - created the file
2020-11-14 01:18:21 +01:00
Casually9790 cd1e8787eb 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 d7185572fc 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 ad944b9616 [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 37b45d998d Update README.md 2020-11-11 23:42:49 +01:00
catoandGitHub 55e2db138a Update README.md 2020-11-11 23:42:25 +01:00
catoandGitHub 64c7ed198f Update README.md 2020-11-11 23:42:02 +01:00
Casually9790 65cebb835a Got a server set up to host the VotingSystem.
---------------------------------------------------
Internet address: http://abizeitung-voting.ddns.net
Server IP address: 217.83.93.128
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 496ec030cb encrypted the emails 2020-11-10 23:50:45 +01:00
Casually9790 2a8d083e34 encrypted the database 2020-11-10 23:49:16 +01:00
Casually9790 dbbdf9e24c changes in the database 2020-11-10 23:44:06 +01:00
Casually9790 de5634b10c q 2020-11-10 23:38:09 +01:00
Casually9790 2a2390feac changed category_id 2020-11-10 23:34:37 +01:00
Casually9790 f108d12beb changed username of sql database 2020-11-10 23:30:43 +01:00
Casually9790 3ecfb34e97 transfer database 2020-11-10 23:27:21 +01:00
Casually9790 281f8811ca testing2 2020-11-10 23:13:16 +01:00
Casually9790 64d6228bdd testing 2020-11-10 22:57:36 +01:00
Casually9790 05877782a9 encrypting the database backup 2020-11-10 22:50:39 +01:00
Casually9790 1f810dbde6 initial commit 2020-11-09 23:51:28 +01:00
46 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
Binary file not shown.
+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 adolfinumvoting@gmail.com</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() {
}
}