New Filestructure for Docker

This commit is contained in:
2026-07-31 23:53:14 +02:00
parent 1e10ed4de8
commit 5bcf2a3546
239 changed files with 710 additions and 2000 deletions
BIN
View File
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
RewriteEngine On
# Do not interfere with real files or directories
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# If a matching .php file exists, serve it
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.+)$ $1.php [L]
File diff suppressed because one or more lines are too long
+206
View File
@@ -0,0 +1,206 @@
.msgDiv {
position: fixed;
bottom: 50px;
right: 20px;
display: flex;
align-items: end;
flex-direction: column-reverse;
z-index: 1000;
}
.msgDiv, .msgDiv > * {
box-sizing: border-box;
}
.msgBox,
.confirmBox {
transform: translateX(calc(100% + 40px)) scaleY(0);
height: 0;
margin-top: 0;
padding: 10px 15px;
color: #fff;
background-color: #0b0b0b;
border-left: 4px solid;
border-radius: 2px;
transition: all 1s ease;
}
.msgBox.show,
.confirmBox.show {
transform: translateX(0) scaleY(1);
height: 40px;
margin-top: 10px;
}
.msgBox.show {
height: 40px;
}
.confirmBox.show {
height: 100px;
}
.buttonConfirmDiv {
margin-top: 16px;
display: flex;
justify-content: space-around;
}
.confirmBox button {
padding: 8px 24px;
transition: background-color 0.3s ease, opacity 1s ease, height 1s ease;
color: #fff;
border: none;
border-radius: 8px;
opacity: 0;
height: 0;
font-size: 18px;
}
.confirmBox.show button {
opacity: 1;
height: auto;
}
.confirmYesButton {
background-color: rgba(0, 255, 0, 0.75);
}
.confirmNoButton {
background-color: rgba(255, 0, 0, 0.75);
}
.confirmYesButton:hover {
background-color: rgba(0, 255, 0, 1);
}
.confirmNoButton:hover {
background-color: rgba(255, 0, 0, 1);
}
.confirmImportantHeading {
font-size: 22px;
margin: 0;
letter-spacing: 1px;
color: #36454F;
}
.confirmImportantText {
font-size: 16px;
margin: 0;
color: #36454F;
}
.confirmImportantLabel {
font-size: 12px;
margin: 0;
color: #36454F;
}
.confirmImportantInput {
border: 1px solid #36454F;
padding: 12px;
width: 100%;
border-radius: 12px;
color: #36454F;
}
.confirmImportantInput.notOk {
color: #692727;
}
.confirmImportantInput.ok {
color: #2b6927;
}
.confirmImportantInput:focus {
outline: none;
}
.confirmImportantInput.notOk:focus {
color: #8b0000;
border-color: #692727;
}
.confirmImportantInput.ok:focus {
color: #008b00;
border-color: #2b6927;
}
.confirmImportantInputDiv {
display: grid;
gap: 6px;
}
.confirmImportantConfrimBtn, .confirmImportantCancelBtn {
background: #25789e;
border: 1px solid #fff;
color: #ffffff;
padding: 10px 20px;
border-radius: 10px;
transition: transform 0.3s ease filter 0.5s ease;
letter-spacing: 1px;
display: flex;
align-self: flex-start;
}
.confirmImportantConfrimBtn {
background: #25789e;
}
.confirmImportantCancelBtn {
background: #9e253d;
}
.confirmImportantInputDiv button:hover {
transform: scale(1.02);
}
.confirmImportantInputDiv button:active {
transform: scale(0.98);
}
.notValidBtn {
user-select: none;
cursor: not-allowed;
filter: grayscale(1);
}
.confirmImportantBtnDiv {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.confirmImportantTextDiv {
display: grid;
gap: 12px;
grid-template-columns: 1fr;
}
.confirmImportantBox {
display: grid;
padding: 20px;
grid-template-columns: 1fr;
position: relative;
border-radius: 10px;
background-color: #fff;
width: min(500px, 90vw);
gap: 18px;
}
.confirmImportantBg {
display: flex;
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
z-index: 10000;
align-items: center;
justify-content: center;
backdrop-filter: blur(2px);
background-color: #36454f2f;
}
+92
View File
@@ -0,0 +1,92 @@
.customSelect {
position: relative;
}
.customSelect>* {
margin: 0;
}
.selectTrigger .selectArrow {
transition: rotate 0.6s ease;
}
table input {
height: 38px;
}
.selectTrigger,
.selectTriggerBulk {
background: none;
border: none;
padding: 0;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
width: 100%;
}
.selectOptions,
.selectOptionsBulk {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
padding: 4px;
list-style: none;
border: 1px solid var(--color-border);
border-radius: 12px;
background: #fff;
z-index: 1000;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
opacity: 0;
transform: scaleY(0);
transform-origin: top;
pointer-events: none;
transition: opacity 0.2s ease, transform 0.2s ease;
}
.customSelect.open .selectOptions,
.customSelect.open .selectOptionsBulk {
opacity: 1;
transform: scaleY(1);
pointer-events: auto;
}
.customSelect.open .selectArrow {
rotate: 180deg
}
.selectOptions li,
.selectOptionsBulk li {
padding: 10px 14px;
cursor: pointer;
border-radius: 8px;
margin-bottom: 2px;
transition: background-color 0.15s ease;
}
.selectOptions li:last-child,
.selectOptionsBulk li:last-child {
margin-bottom: 0;
}
.selectOptions li:hover,
.selectOptionsBulk li:hover {
background-color: #eee;
}
.selectOptions li.selected,
.selectOptionsBulk li.selected {
background-color: #ccc;
color: #000;
font-weight: 500;
}
.selectLabel:not(.sidebar-links .selectLabel) {
font-size: 1rem;
}
+187
View File
@@ -0,0 +1,187 @@
:root {
--main: #6e285f;
--bg-top-raw: 110 40 95;
--main-box-shadow: rgb(from var(--main) r g b / 0.05);
--accent: #737444;
--paddingSite: 40px;
--card-radius: 20px;
--card-bg: #ffffff;
--bg: #FDFDFD;
--card-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
--accent-soft: #f0f5ff;
--border-subtle: #d4d7e1;
--text-main: #191919;
--text-muted: #5e5e5e;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body,
section {
margin: 0;
overscroll-behavior: none;
}
body {
margin: 0;
background-color: var(--bg);
color: var(--text-main);
}
.bgSection {
padding: var(--paddingSite);
}
.headerDivTrainer {
padding: var(--paddingSite);
background-color: var(--main);
color: var(--bg);
margin-bottom: var(--paddingSite);
display: flex;
justify-content: space-between;
align-items: center;
}
.headingPanel {
margin: 0;
font-weight: 200;
}
.controls-wrapper {
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
margin-bottom: var(--paddingSite);
flex-wrap: wrap;
}
.controls-wrapper>div,
.controls-wrapper>form {
display: flex;
flex-direction: row;
gap: 10px;
flex-wrap: wrap;
}
/* From Trainer Dashboard - newBtn style */
button.change-type,
button[type="submit"] {
cursor: pointer;
font-weight: 700;
transition: all 0.2s;
padding: 10px 24px;
border-radius: 100px;
background: #cfef00;
border: 1px solid transparent;
display: flex;
align-items: center;
font-size: 15px;
color: #000;
}
button.change-type:hover,
button[type="submit"]:hover {
background: #c5e300;
transform: translateY(-1px);
}
button.change-type:active,
button[type="submit"]:active {
transform: scale(0.95);
}
.change-type-form {
display: flex;
align-items: center;
}
.change-type-form textarea {
padding: 10px 15px;
border-radius: 8px;
border: 1px solid var(--border-subtle);
background: #fff;
font-size: 15px;
width: 250px;
outline: none;
transition: border-color 0.2s;
}
.change-type-form textarea:focus {
border-color: var(--main);
}
.iframe-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 30px;
}
.iframeWithTitle {
background: #000;
border-radius: 20px;
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: var(--card-shadow);
}
@media (max-width: 500px) {
:root {
--paddingSite: 20px;
}
.iframeWithTitle {
max-width: calc(100vw - 2 * var(--paddingSite));
}
}
.iframeWithTitle h1 {
color: #fff;
font-size: 1.2rem;
margin-bottom: 15px;
font-weight: 400;
}
iframe {
width: 100%;
aspect-ratio: 16/9;
border: none;
border-radius: 15px;
background: #222;
}
.divSucsess {
display: flex;
opacity: 0;
position: fixed;
top: 20px;
right: 20px;
transform: translateX(400px);
background-color: #ffffff;
z-index: 10000;
border-radius: 12px;
box-shadow: var(--card-shadow);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
border-left: 5px solid #4ade80;
padding-right: 20px;
}
.divSucsess.show {
opacity: 1;
transform: translateX(0);
}
.textSucsess {
color: #137333;
margin: 15px;
font-weight: 600;
font-size: 14px;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+147
View File
@@ -0,0 +1,147 @@
body{
overflow: hidden;
}
* {
box-sizing: border-box;
}
.page-secure-login{
display: flex;
}
.bg-secure-login-form {
gap: 20px;
display: grid;
}
.bg-picture-secure-login{
width: calc(100vw - 550px);
height: 100vh;
position: absolute;
left: 0px;
top: 0px;
background-color: #fff;
}
.bg-picture-secure-login img{
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 0 80px 0 0;
display: block;
}
.bg-secure-login{
display: flex;
width: 100vw;
max-width: 550px;
height: 100vh;
background-color: #fff;
position: absolute;
right: 0px;
top: 0px;
align-items: center;
justify-content: center;
padding: 30px;
}
.bg-secure-login-form > h1{
color: #36454F !important;
font-size: 32px;
font-weight: 500;
letter-spacing: 1px;
}
.bg-secure-login-form input[type=password], .bg-secure-login-form input[type=text]{
padding: 5px;
width: 100%;
max-width: 300px;
border-top: none !important;
border-left: none !important;
border-right: none !important;
font-size: 16px;
border-bottom: 1px solid #000 !important;
border-radius: 0px !important;
}
#access_username {
margin-bottom: 20px;
}
.bg-secure-login-form input[type=password]:focus, .bg-secure-login-form input[type=text]:focus{
outline: none;
border-bottom: 1px solid #000 !important;
}
.bg-secure-login-form input[type=password]::placeholder, .bg-secure-login-form input[type=text]::placeholder {
color: #ccc !important;
}
.bg-secure-login-form input[type=submit]{
background-color: #fff !important;
padding: 10px 20px !important;
margin-top: 25px !important;
border: 1px solid #000 !important;
color: #36454F !important;
transition: all 0.3s ease-out !important;
border-radius: 0px !important;
font-weight: 500;
letter-spacing: 1px;
}
.bg-secure-login-form input[type=submit]:hover{
background-color: #36454F !important;
color: #fff !important;
}
.bg-secure-login-form > p{
margin-bottom: 30px;
}
.seclog_home_link{
position: fixed;
z-index: 1000;
top: 30px;
right: 30px;
height: 48px;
width: 48px;
}
.seclog_home_link > img{
object-fit: cover;
width: 100%;
}
#div_showpw, #access_username {
margin-top: 10px;
}
#togglePassword {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
transition: all 0.5s ease;
}
#togglePassword:hover {
transform: translateY(-50%) scale(1.15);
}
#div_showpw{
position: relative;
display: inline-block;
width: 100%;
max-width: 300px;
}
label {
color: #36454F !important;
font-weight: 300;
letter-spacing: 1px;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px #ffffff inset !important;
box-shadow: 0 0 0 1000px #ffffff inset !important;
-webkit-text-fill-color: #000000 !important;
transition: background-color 5000s ease-in-out 0s;
}
File diff suppressed because it is too large Load Diff
+120
View File
@@ -0,0 +1,120 @@
body{
overflow: hidden;
}
.page-secure-login{
display: flex;
}
.bg-picture-secure-login{
width: calc(100vw - 450px);
height: 100vh;
position: absolute;
left: 0px;
top: 0px;
}
.bg-picture-secure-login img{
width: 100%;
height: 100vh;
object-fit: cover;
}
.bg-secure-login{
display: flex;
width: 100vw;
max-width: 450px;
height: 100vh;
background-color: #fff;
position: absolute;
right: 0px;
top: 0px;
align-items: center;
padding: 30px;
}
.bg-secure-login-form > h1{
color: #000 !important;
font-size: 32px;
}
.bg-secure-login-form input[type=password], .bg-secure-login-form input[type=text]{
padding: 5px;
width: 100%;
max-width: 300px;
border-top: none !important;
border-left: none !important;
border-right: none !important;
font-size: 16px;
border-bottom: 1px solid #000 !important;
border-radius: 0px !important;
}
.divShowPw:not(#lastDivShowPw) {
margin-bottom: 20px;
}
.bg-secure-login-form input[type=password]:focus, .bg-secure-login-form input[type=text]:focus{
outline: none;
border-bottom: 1px solid #000 !important;
}
.bg-secure-login-form input[type=password]::placeholder, .bg-secure-login-form input[type=text]::placeholder {
color: #ccc !important;
}
.bg-secure-login-form input[type=submit]{
background-color: #fff !important;
padding: 10px 20px !important;
margin-top: 25px !important;
border: 1px solid #000 !important;
color: #000 !important;
transition: all 0.3s ease-out !important;
border-radius: 0px !important;
}
body {
color: #000 !important;
}
* {
box-sizing: border-box;
}
.bg-secure-login-form input[type=submit]:hover{
background-color: #000 !important;
color: #fff !important;
}
.bg-secure-login-form > p{
margin-bottom: 30px;
}
.seclog_home_link{
position: fixed;
z-index: 1000;
top: 30px;
right: 30px;
}
.divShowPw {
margin-top: 10px;
position: relative;
display: inline-block;
width: 100%;
max-width: 300px;
}
.togglePassword {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
transition: all 0.5s ease;
}
.togglePassword:hover {
transform: translateY(-50%) scale(1.15);
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px #ffffff inset !important;
box-shadow: 0 0 0 1000px #ffffff inset !important;
-webkit-text-fill-color: #000000 !important;
transition: background-color 5000s ease-in-out 0s;
}
+585
View File
@@ -0,0 +1,585 @@
:root {
--paddingSite: 40px;
--card-radius: 24px;
--card-bg: #ffffff;
--bg: #F4F3EF;
--card-shadow: 0 12px 40px rgba(0, 0, 0, 0.04);
--accent: #FF5A5F;
/* Soft energetic pink/red from Dribbble */
--accent-secondary: #FF8A65;
--bg-top: rgb(54, 137, 13);
--bg-top-raw: 54 137 13;
--accent-soft: #f0f5ff;
--border-subtle: #eaeaea;
--text-main: #1b1b1b;
--text-muted: #8A8A8A;
--disabled-bg: #f3f4f6;
--disabled-border: #cbd5f5;
--ui-border: #dfe4ea;
--main-button: #36454F;
}
/* Base */
*,
*::after,
*::before {
box-sizing: border-box;
}
html,
body,
section {
margin: 0;
overscroll-behavior: none;
}
body {
margin: 0;
width: 100vw;
background: var(--bg);
}
::selection {
background: #0000003d;
color: #ffffffff;
}
::-moz-selection {
background: #a4bf4a99;
color: #000;
}
input {
border: none;
}
/* General numeric inputs in scoring area */
.bgSection input[type="number"] {
font-size: 16px;
}
.nopadding {
height: 40px;
}
/* Layout */
.bgSection {
position: relative;
margin-left: auto;
/*margin: var(--paddingSite);
width: calc(100vw - 2 * var(--paddingSite));*/
width: 100vw;
box-sizing: border-box !important;
background: none;
}
@media (min-width: 1081px) {
.bgSection.open {
width: calc(100vw - 380px);
/* - 2 * var(--paddingSite) */
}
}
/* Cards */
.headerDivTrainer {
padding: var(--paddingSite);
padding-bottom: 10px;
background: transparent;
margin-bottom: 20px;
position: relative;
z-index: 3;
}
.dashboardGrid {
padding: 0 var(--paddingSite) var(--paddingSite);
display: grid;
grid-template-columns: 1fr;
gap: 30px;
align-items: start;
width: 100%;
}
.twoColumDiv {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
align-items: start;
width: 100%;
}
@media (max-width: 1080px) {
.twoColumDiv {
grid-template-columns: 1fr;
}
}
/* headings */
.containerHeading {
margin-top: 0;
font-weight: 600;
font-size: 20px;
margin-bottom: 24px;
color: var(--text-main);
}
.subSettingsRowGroup .settingsRow span {
color: #36454f;
}
.subSettingsRowGroup {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
align-items: end;
gap: 16px 22px;
padding: 20px;
border: 1px solid #000;
}
.customSelectSubGroupHeader {
border-bottom: 1px solid #1c2d34;
font-weight: 600;
border-radius: 0 !important;
pointer-events: none;
}
.customSelectAddOption {
display: inline-flex;
}
.headerThRankLiveConfig > span {
display: flex;
justify-content: space-between;
position: relative;
padding: 20px;
border: 1px solid #00000046;
}
#bodyRowRankLiveConfig > td > span {
display: flex;
justify-content: flex-end;
gap: 15px;
position: relative;
padding: 10px 20px;
border: 1px solid #00000046;
position: relative;
min-height: 54.5px;
align-items: center;
}
.headerThRankLiveConfig .addColumnBtn {
font-size: 24px;
height: 30px;
width: 30px;
line-height: 0;
position: absolute;
top: 0px;
background-color: #5d9ab9;
border: 1px solid #3a6b83;
color: #fff;
border-radius: 15px;
align-content: center;
cursor: pointer;
z-index: 3;
}
.headerThRankLiveConfig .addColumnBtn.addColumnBtnAfter {
right: 0;
transform: translateX(50%) translateY(-50%);
}
.headerThRankLiveConfig .addColumnBtn.addColumnBtnBefore {
left: 0;
transform: translateX(-50%) translateY(-50%);
}
.headerThRankLiveConfig .addColumnBtn {
display: none;
}
.headerThRankLiveConfig:not(.ui-sortable-helper) > span:hover .addColumnBtn.addColumnBtnAfter {
display: block;
}
.headerThRankLiveConfig:not(.ui-sortable-helper):first-child > span:hover .addColumnBtn.addColumnBtnBefore {
display: block;
}
.headerThRankLiveConfig:not(.ui-sortable-helper) > span:hover {
border-right: 1px solid #3a6b83;
}
.headerThRankLiveConfig:not(.ui-sortable-helper):first-child > span:hover {
border-left: 1px solid #3a6b83;
}
.deleteColumnBtn {
color: #6a2a2a;
cursor: pointer;
}
.customSelectAddOption {
max-width: min(100vw, 600px);
border: 1px solid #777;
border-radius: 5px;
padding: 10px;
}
.rankLiveFieldContainerEmty {
display: block;
min-height: 30px;
width: 100%;
background: repeating-linear-gradient(
45deg,
#eeeeee50,
#eeeeee50 5px,
#dddddd50 5px,
#dddddd50 10px
);
}
.singleValueSpan {
position: relative;
padding: 5px;
border-radius: 5px;
border: 1px solid #777;
cursor: grab;
}
.staticText {
border-color: #00f;
background-color: #fff;
}
.staticText .textSingleValueSpan {
white-space: pre;
}
.settingSpan {
display: none;
padding: 2px;
border-radius: 3px;
background-color: #09d1ce;
position: absolute;
top: 0px;
transform: translateX(50%) translateY(-50%);
cursor: pointer;
}
.headerThRankLiveConfig .settingSpan {
right: 50%;
}
.singleValueSpan .settingSpan {
right: 0px;
}
.mainTable:not(.selectCondEl) .singleValueSpan:hover .settingSpan,
.mainTable:not(.selectCondEl) .headerThRankLiveConfig:hover .settingSpan,
.settingsDivSingleValueSpan:not(.hidden) + .settingSpan {
display: inline-flex;
}
.settingSpan > svg {
display: block;
height: 16px;
width: 16px;
pointer-events: none;
}
.settingsDivSingleValueSpan {
background-color: #fff;
padding: 15px;
position: absolute;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
transform: translateY(100%);
min-width: 100%;
width: fit-content;
gap: 12px;
z-index: 10;
font-weight: 400;
}
.settingsInputGroup {
display: flex;
flex-direction: column;
gap: 8px;
}
.singleValueSpan[data-bold="true"] .textSingleValueSpan, .singleValueSpan[data-bold="true"] .statischerTextInput {
font-weight: 600;
}
.addFieldBtn {
position: absolute;
top: 0;
left: 0;
background-color: #36e049;
display: flex;
height: 30px;
width: 30px;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 800;
border-radius: 0 0 10px 0;
filter: grayscale(1);
transform: translateY(-37.5%) translateX(-37.5%) scale(0.25);
transition: transform 0.3s ease, scale 0.3s ease;
cursor: pointer;
z-index: 2;
}
.mainTable.selectCondEl .addFieldBtn {
transform: scale(0) translateY(-50%) translateX(-50%);
scale: 1;
}
.mainTable:not(.selectCondEl) .rankLiveFieldContainer:hover:not(:has(.singleValueSpan:hover)) .addFieldBtn {
filter: grayscale(0);
transform: scale(1) translateY(0) translateX(0);
scale: 1;
}
.headerThRankLiveConfig > span .deleteColumnBtn {
position: absolute;
top: 0;
right: 0;
background-color: #e03669;
display: flex;
height: 30px;
width: 30px;
justify-content: center;
align-items: center;
color: #fff;
font-weight: 800;
border-radius: 0 0 0 10px;
filter: grayscale(1);
transform: translateY(-37.5%) translateX(37.5%) scale(0.25);
transition: transform 0.3s ease, scale 0.3s ease;
cursor: pointer;
}
.headerThRankLiveConfig:not(.ui-sortable-helper) > span:hover .deleteColumnBtn {
filter: grayscale(0);
transform: scale(1) translateY(0) translateX(0);
scale: 1;
}
.checkbox {
display: flex;
align-items: center;
justify-content: center;
width: max-content;
cursor: pointer;
user-select: none;
}
.checkbox input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
}
.checkbox-ui {
position: relative;
display: inline-flex;
width: 42px;
height: 24px;
flex: 0 0 auto;
background: var(--ui-border);
border: 1px solid color-mix(in srgb, var(--ui-border) 80%, #000);
border-radius: 999px;
transition:
background-color 180ms ease,
border-color 180ms ease,
box-shadow 180ms ease;
}
.checkbox-ui::after {
content: "";
position: absolute;
top: 50%;
left: 3px;
width: 18px;
height: 18px;
background: #fff;
border-radius: 50%;
box-shadow: 0 2px 7px rgba(15, 23, 42, 0.22);
transform: translateY(-50%);
transition: transform 180ms ease;
}
.checkbox input:checked + .checkbox-ui {
background: var(--bg-top);
border-color: var(--bg-top);
}
.checkbox input:checked + .checkbox-ui::after {
transform: translate(18px, -50%);
}
.checkbox input:focus-visible + .checkbox-ui {
box-shadow: 0 0 0 4px var(--ui-focus);
}
.dropPlaceholder {
border: 2px dashed #ccc;
border-radius: 8px;
}
.dropPlaceholderColumn {
border: 2px dashed #ddd;
border-radius: 2px;
}
.statischerTextInput {
border-bottom: 1px solid #000;
}
.statischerTextInput:focus {
border-color: var(--bg-top);
outline: none;
}
.columnTitleInput, .inputFontSize, .gebutsdatumFormatInput, .inputDisplayOnDisplayWidth {
background: none;
padding: 2px;
margin-right: 10px;
height: auto;
width: 100%;
max-width: 20vw;
border-bottom: 1px solid #000;
}
.columnTitleInput:focus {
border-color: var(--bg-top);
}
.customSelectAddOption .selectOptions {
width: fit-content;
}
.customSelectAddOption {
position: absolute !important;
background: #fff;
}
.inputFontSize {
max-width: 60px;
}
.inputDisplayOnDisplayWidth {
width: 90px;
}
.autocomplete-container {
position: relative;
width: 100%;
}
.dropdown-list {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #ccc;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
}
.dropdown-item {
padding: 8px;
cursor: pointer;
}
.dropdown-item:hover {
background-color: #f0f0f0;
}
.verticalLayoutEl {
display: grid;
grid-template-columns: 1fr;
}
.verticalLayoutEl > span.dragableDropArea {
min-height: 20px;
min-width: max(50px, 100%);
padding: 12px;
display: flex;
width: fit-content;
justify-content: center;
}
.verticalLayoutEl > span.dragableDropArea:not(:last-child) {
border-bottom: 1px solid #000;
}
.inputLabelWrapper {
display: flex;
flex-direction: row;
}
.selectSlugType {
max-width: 300px;
}
.customSelectSlugType {
border: 1px solid #000;
padding: 6px 12px;
border-radius: 6px;
}
.selectCondEl.mainTable .singleValueSpan:not(.condEl) {
border-color: #a5a5a5;
color: #a5a5a5;
background-color: #ececec;
}
.selectCondEl.mainTable .singleValueSpan:not(.condEl):hover {
border-color: #0fab91;
color: #0fab91;
background-color: #ffffff;
}
.selectCondEl.mainTable .singleValueSpan.selectedCondEl {
border-color: #ab4b0f;
color: #ab4b0f;
background-color: #ffffff;
}
.hidden {
display: none !important;
}
.blackBtn {
background: var(--main-button);
border: 1px solid #fff;
color: #ffffff;
padding: 10px 20px;
border-radius: 10px;
transition: transform 0.3s ease;
letter-spacing: 1px;
display: inline-flex;
align-self: flex-start;
font-size: 14px;
}
.blackBtn:hover {
transform: scale(1.02);
}
.blackBtn:active {
transform: scale(0.98);
}
File diff suppressed because it is too large Load Diff
+637
View File
@@ -0,0 +1,637 @@
:root {
--main: #fe3f18;
--bg-top-raw: 254 63 24;
--main-box-shadow: rgb(from var(--main) r g b / 0.05);
--accent: #fe3f18;
--accent-hover: #c5e300;
--paddingSite: 40px;
--card-radius: 20px;
--card-bg: #ffffff;
--bg: #FDFDFD;
--card-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
--border-subtle: #d4d7e1;
--text-main: #191919;
--text-muted: #5e5e5e;
--main-button: #36454F;
}
/* --- BASE & TYPOGRAPHY --- */
*,
*::before,
*::after {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
b {
font-weight: 600;
}
body {
margin: 0;
width: 100vw;
background: var(--bg);
color: var(--text-main);
overflow-x: hidden;
overscroll-behavior: none;
font-optical-sizing: auto;
}
/* --- LAYOUT WRAPPERS --- */
.bgSection {
position: relative;
width: 100vw;
margin-left: auto;
box-sizing: border-box;
background: none;
min-height: 100vh;
}
@media (min-width: 1200px) {
.bgSection.open {
width: calc(100vw - 450px);
}
}
.menuTransition {
transition: transform 0.45s cubic-bezier(.4, 0, .2, 1), width 0.45s cubic-bezier(.4, 0, .2, 1) !important;
}
/* --- HEADER --- */
.headerDivKampfrichter {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--paddingSite);
background: var(--main);
color: #fff;
margin-bottom: var(--paddingSite);
position: relative;
z-index: 3;
}
.heading-pannel {
margin: 0;
font-weight: 300;
font-size: 2rem;
letter-spacing: -0.5px;
}
.menuWrapper {
display: flex;
align-items: center;
gap: 24px;
}
.mainContentDiv {
padding: 0 var(--paddingSite);
}
/* --- FOOTER / LOGOUT --- */
.footerInternMenu {
margin-top: auto;
padding-top: 20px;
}
/* Burger + menu (unchanged from your style) */
.wk-leitungBurgerMenuDiv {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
cursor: pointer;
z-index: 99;
color: var(--bg);
}
.wk-leitungBurgerMenuDiv svg {
width: 100%;
height: 100%;
stroke: currentColor;
transition: transform 0.3s ease;
}
.wk-leitungBurgerMenuDiv.open svg {
transform: rotate(45deg);
}
.internMenuDiv {
background-color: #fff;
box-shadow: none;
position: fixed;
left: 0;
top: 0;
height: 100svh;
width: 100%;
max-width: 450px;
transform: translateX(-100%);
z-index: 100;
padding: 30px;
transition: transform 0.45s cubic-bezier(.4, 0, .2, 1);
opacity: 1;
}
.internMenuDiv.open {
transform: translateX(0);
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.15);
}
.innerInternMenuDiv {
display: flex;
flex-direction: column;
gap: 14px;
height: 100%;
overflow-y: auto;
}
.text_akt_abt {
font-size: 1rem;
color: #555;
text-align: center;
line-height: 1.6;
}
.innerInternMenuDiv form {
margin: 0;
}
.innerInternMenuDiv input[type="submit"],
.innerInternMenuDiv select {
width: 100%;
padding: 12px 14px;
border-radius: 10px;
border: 1px solid #ccc;
font-size: 0.95rem;
transition: all 0.3s ease;
}
.innerInternMenuDiv input[type="submit"]:focus,
.innerInternMenuDiv select:focus {
outline: none;
border-color: #2d73ac;
box-shadow: 0 0 6px rgba(45, 115, 172, 0.3);
}
.innerInternMenuDiv .button-secondary {
background: linear-gradient(135deg, #2d73ac, #1f4f75);
color: #fff;
border: none;
border-radius: 12px;
font-weight: 600;
cursor: pointer;
padding: 12px 18px;
transition: transform 0.2s ease, background 0.3s ease;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.innerInternMenuDiv .button-secondary:hover {
background: linear-gradient(135deg, #245d8a, #163b5a);
transform: translateY(-2px);
}
/* Riegeneinteilung Specific Styles */
.headerAbt {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 8px;
}
.headerAbt h2 {
margin: 0;
font-weight: 400;
color: var(--main);
font-size: 1.5rem;
}
.textInputAbtTitel {
margin: 0;
font-weight: 400;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 1px solid var(--main);
font-size: 1.5rem;
color: #191919;
background: none;
width: max-content;
padding: 4px 8px;
}
.deleteProgramm {
display: flex;
align-items: center;
justify-content: center;
background: #fff;
border: 1px solid #fee2e2;
border-radius: 50%;
width: 36px;
height: 36px;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(244, 63, 94, 0.1);
}
.deleteProgramm:hover {
transform: scale(1.1);
background: #fee2e2;
box-shadow: 0 4px 8px rgba(244, 63, 94, 0.2);
}
.deleteProgramm svg,
.deleteProgramm path {
fill: #f43f5e;
}
#bin-lid {
transform-origin: 50% 100%;
transform-box: fill-box;
transition: transform 0.3s ease;
}
.deleteProgramm:hover #bin-lid {
transform: rotate(20deg) translateY(-100px);
}
/* Sidebar Menu Button Styling */
.headerOptionConatiner {
display: flex;
flex-direction: column;
padding-top: 20px;
gap: 15px;
margin-top: auto;
}
.headerOptionConatiner > button {
}
.headerOptionConatiner label {
font-weight: 400;
color: var(--text-main);
font-size: 0.9rem;
margin-bottom: -5px;
}
.headerOptionConatiner input[type="number"] {
padding: 12px 15px;
border-radius: 12px;
border: 1px solid var(--border-subtle);
background: #fff;
font-size: 15px;
outline: none;
transition: all 0.2s ease;
width: 100%;
}
.headerOptionConatiner input[type="number"]:focus {
border-color: var(--main);
box-shadow: 0 0 0 3px rgba(40, 102, 110, 0.1);
}
/* Custom Table / Drag and Drop Area */
.geraet-table {
background: var(--card-bg);
border: 1px solid var(--border-subtle);
border-radius: 16px;
box-shadow: var(--card-shadow);
min-width: 260px;
overflow: hidden;
margin-right: 15px;
transition: all 0.3s ease;
border-collapse: separate;
border-spacing: 0;
width: 100%;
}
.geraet-table thead {
background: var(--main);
}
.geraet-table thead th {
display: flex;
justify-content: space-between;
color: #fff;
padding: 14px 20px;
font-weight: 300;
font-size: 1.05rem;
text-align: left;
letter-spacing: 0.5px;
border-bottom: 1px solid var(--border-subtle);
}
.geraet-table thead th i span {
font-size: 0.9rem;
opacity: 0.8;
font-weight: 400;
}
.geraet-table tbody {
display: block;
max-height: 480px;
overflow-y: auto;
overscroll-behavior-y: contain;
padding: 10px;
}
/* Custom Scrollbar for tbody */
.geraet-table tbody::-webkit-scrollbar {
width: 6px;
}
.geraet-table tbody::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 10px;
}
.geraet-table tbody::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 10px;
}
.geraet-table tbody::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
.turnerin-row {
cursor: grab;
background: #fff;
border-radius: 8px;
margin-bottom: 8px;
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s ease;
display: block;
border: 1px solid transparent;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
.turnerin-row:active {
cursor: grabbing;
}
.turnerin-row>td {
padding: 12px 16px;
border: none;
display: block;
width: 100%;
color: var(--text-main);
font-weight: 200;
}
.turnerin-row:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
transform: translateY(-2px);
z-index: 10;
position: relative;
border-color: #cbd5e1;
}
/* Drag State */
.dragging {
opacity: 0.9 !important;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15) !important;
transform: scale(1.03) rotate(1deg) !important;
z-index: 9999 !important;
}
.drop-placeholder {
height: 54px;
background: #f8fafc;
border: 2px dashed #94a3b8;
border-radius: 8px;
margin: 8px 0;
}
.ui-sortable {
min-height: 120px;
}
.empty-drop-row td {
padding: 20px;
text-align: center;
color: var(--text-muted);
font-style: italic;
}
/* Grouping Row Styling */
.group-row {
background: transparent;
margin-bottom: 12px;
}
.group-row>td {
padding: 0;
display: block;
}
.group-header {
padding: 12px 16px;
cursor: grab;
gap: 18px;
border-radius: 10px;
font-weight: 200;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
margin-bottom: 6px;
display: flex;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
justify-content: space-between;
align-items: center;
}
.group-header:active {
cursor: grabbing;
}
.group-inner {
width: 100%;
border-collapse: separate;
border-spacing: 0;
padding-left: 10px;
}
.group-row .group-inner {
display: none;
}
/* Invalid / Unassigned Wrapper */
.invalidAbtDiv {
padding: 10px 0;
}
.invalidAbtDiv .geraet-table {
box-shadow: none;
border-color: #d93f4a;
width: 100%;
}
.invalidAbtDiv .geraet-table thead {
background: #d93f4a;
}
.invalidAbtDiv .geraet-table thead th {
color: #ffffff;
}
.singleAbtDiv {
padding-bottom: 20px;
}
.singleAbtDataContainer {
overflow-x: auto;
padding: 10px 0 20px 0;
display: flex;
flex-direction: row;
gap: 16px;
align-items: flex-start;
margin-top: 24px;
margin-bottom: 8px;
}
.singleAbtDataContainer::-webkit-scrollbar {
height: 8px;
}
.singleAbtDataContainer::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 10px;
}
.singleAbtDataContainer::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 10px;
}
.disabled {
opacity: 0.5;
pointer-events: none;
}
.buttonMoveDiv {
display: flex;
flex-direction: column;
gap: 4px;
}
.buttonMove {
background: #fff;
border: 1px solid #d1d5db;
border-radius: 6px;
width: 24px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
input.abtIndexInput::-webkit-outer-spin-button,
input.abtIndexInput::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input.abtIndexInput[type=number] {
-moz-appearance: textfield;
}
.abtIndexInput {
text-align: center;
}
.nichtBezahlt {
color: #e11d48;
font-size: 0.9rem;
font-weight: 500;
}
.timePicker {
border: none;
border-bottom: 1px solid #000;
background: none;
padding: 4px 8px;
font-size: 1rem;
color: var(--main);
width: 80px;
text-align: center;
}
.addTimetypeButton {
color: #2dac79;
}
.removeTimetypeButton {
color: #e11d48;
}
.subHeaderAbt {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 4px 8px;
background: #f1f5f9;
border-radius: 8px;
font-size: 0.9rem;
align-self: start;
margin-top: 18px;
position: relative;
}
.subHeaderAbt .customSelect {
position: absolute;
width: 160px;
bottom: 0px;
transform: translateY(120%) translateX(-10%);
padding: 6px;
background: #fff;
border: 1px solid #7b7676;
border-radius: 6px;
}
.hidden {
display: none !important;
}
.buttonsDiv {
display: flex;
flex-direction: column;
gap: 12px;
justify-content: flex-start;
margin-bottom: 20px;
position: fixed;
bottom: 10px;
right: 20px;
}
.blackBtn {
background: var(--main-button);
border: 1px solid #fff;
color: #ffffff;
padding: 10px 20px;
border-radius: 10px;
transition: transform 0.3s ease;
letter-spacing: 1px;
display: flex;
align-self: flex-start;
}
.blackBtn:hover {
transform: scale(1.02);
}
.blackBtn:active {
transform: scale(0.98);
}
h3.normalTitle {
color: var(--main-button);
margin-bottom: 8px;
letter-spacing: 1px;
}
+341
View File
@@ -0,0 +1,341 @@
/* ─── Sidebar Navigation ─── */
:root {
--sidebar-width: 280px;
--sidebar-bg: #111218;
/* Dark navy / charcoal */
--sidebar-text: #A0A0A0;
/* Soft grey text */
--sidebar-hover: rgba(255, 255, 255, 0.05);
/* Subtile hover effect */
--sidebar-active: rgba(255, 255, 255, 0.1);
--sidebar-transition: 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Sidebar Panel */
.sidebar-nav {
position: fixed;
top: 0;
left: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
z-index: 10000;
transform: translateX(-100%);
transition: transform var(--sidebar-transition), box-shadow var(--sidebar-transition);
display: flex;
flex-direction: column;
overflow-y: auto;
box-shadow: none;
}
.sidebar-nav.open {
transform: translateX(0);
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.25);
}
/* Sidebar Header */
.sidebar-header {
padding: 28px 24px 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
align-items: center;
justify-content: space-between;
}
.sidebar-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #FFFFFF;
letter-spacing: 0.5px;
}
.sidebar-close-btn {
background: none;
border: none;
color: var(--sidebar-text);
cursor: pointer;
padding: 4px;
display: flex;
align-items: center;
border-radius: 6px;
transition: background 0.2s;
}
.sidebar-close-btn:hover {
background: var(--sidebar-hover);
}
/* Section Labels */
.sidebar-section-label {
padding: 20px 24px 8px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.2px;
color: rgba(255, 255, 255, 0.35);
}
/* Links */
.sidebar-links {
list-style: none;
margin: 0;
padding: 0;
flex: 1;
}
.sidebar-links a {
display: flex;
align-items: center;
gap: 14px;
padding: 12px 24px;
color: var(--sidebar-text);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
border-left: 3px solid transparent;
}
.sidebar-links a:hover {
background: var(--sidebar-hover);
color: #FFFFFF;
}
.sidebar-links a.active {
background: var(--sidebar-active);
color: #FFFFFF;
border-left-color: var(--accent);
}
.sidebar-links a svg {
width: 20px;
height: 20px;
flex-shrink: 0;
stroke: currentColor;
fill: none;
stroke-width: 1.8;
stroke-linecap: round;
stroke-linejoin: round;
}
/* Overlay */
.sidebar-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(2px);
z-index: 9999;
opacity: 0;
pointer-events: none;
transition: opacity var(--sidebar-transition);
}
.sidebar-overlay.open {
opacity: 1;
pointer-events: auto;
}
/* Hamburger Toggle */
.sidebar-toggle {
background: #FFFFFF;
border: none;
border-radius: 50%;
width: 48px;
height: 48px;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
transition: all var(--sidebar-transition);
padding: 0;
}
.sidebar-toggle span {
display: block;
width: 20px;
height: 2px;
background: #111;
border-radius: 2px;
transition: all 0.3s;
}
.sidebar-toggle.open span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.sidebar-toggle.open span:nth-child(2) {
opacity: 0;
}
.sidebar-toggle.open span:nth-child(3) {
transform: rotate(-45deg) translate(5px, -5px);
}
/* Footer */
.sidebar-footer {
padding: 16px 24px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
font-size: 11px;
color: rgba(255, 255, 255, 0.4);
}
.menuWrapper {
position: fixed;
right: 20px;
top: 20px;
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
border-radius: 40px;
background-color: rgb(var(--bg-top-raw));
}
.abmeldenbutton {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #e53935, #b71c1c);
border: none;
border-radius: 12px;
font-weight: 600;
color: #fff;
cursor: pointer;
transition: transform 0.2s ease, background 0.3s ease;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.abmeldenbutton:hover {
background: linear-gradient(135deg, #c62828, #8e1919);
transform: translateY(-2px);
}
.sidebarUsername {
padding: 20px 24px 8px;
font-size: 14px;
font-weight: 400;
color: rgb(146 146 146 / 85%);
}
.customSelect {
position: relative;
}
.customSelect>* {
margin: 0;
}
.selectTrigger .selectArrow {
transition: rotate 0.6s ease;
}
table input {
height: 38px;
}
.selectTriggerSidebar {
background: none;
border: none;
padding: 0;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
.sidebar-nav .selectOptionsSidebar {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
padding: 4px;
list-style: none;
margin-top: 2px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 12px;
background: #fff;
z-index: 1000;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
opacity: 0;
transform: scaleY(0);
transform-origin: top;
pointer-events: none;
transition: opacity 0.2s ease, transform 0.2s ease;
}
.sidebar-nav .customSelect.open .selectOptionsSidebar {
opacity: 1;
transform: scaleY(1);
pointer-events: auto;
}
.customSelect.open .selectArrow {
rotate: 180deg
}
.selectOptionsSidebar li {
padding: 10px 14px;
cursor: pointer;
border-radius: 8px;
margin-bottom: 2px;
transition: background-color 0.15s ease;
}
.selectOptionsSidebar li:last-child {
margin-bottom: 0;
}
/* ─── Sidebar Freigaben ─── */
.sidebar-li-freigaben {
padding: 0 24px 16px 42px;
/* Indented under the icon of the active link */
margin-top: 4px;
}
.sidebar-freigaben-label {
display: block;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
color: #fff;
margin-bottom: 6px;
letter-spacing: 0.5px;
}
.selectTriggerSidebar {
width: 100%;
padding: 9px 12px;
background: #171717;
border: 1px solid #fcfcfc;
border-radius: 8px;
font-size: 13px;
font-weight: 500;
color: #fcfcfc;
transition: all 0.2s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
}
.selectTriggerSidebar:hover, .sidebar-li-freigaben .customSelect.open .selectTriggerSidebar {
background: #fcfcfc;
color: #171717;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}
.selectOptionsSidebar li {
font-size: 13px;
padding: 8px 12px;
color: #000;
}
.selectOptionsSidebar li.selected {
background: var(--sidebar-active);
color: #111;
font-weight: 600;
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
/* Dies ist eine CSS-Datei, welche mit eigener CSS-Logik gefüllt werden kann. Diese CSS Datei glit für alle Intern Seiten.*/
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+5
View File
@@ -0,0 +1,5 @@
Alle Bilder für Intern müssen nach folgendem Schema benennt werden:
"Bg" + $typ ".webp"
für $typ gilt: _ sowie ucfirst
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

+1
View File
@@ -0,0 +1 @@
Die Intern Seiten suchen das Icon in diesem Order mit dem Namen icon.png
File diff suppressed because one or more lines are too long
+632
View File
@@ -0,0 +1,632 @@
ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"], function(require, exports, module){"use strict";
var oop = require("../lib/oop");
var lang = require("../lib/lang");
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var supportType = exports.supportType = "align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|max-zoom|min-height|min-width|min-zoom|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|user-select|user-zoom|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index";
var supportFunction = exports.supportFunction = "rgb|rgba|url|attr|counter|counters";
var supportConstant = exports.supportConstant = "absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|flex-end|flex-start|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero|zoom";
var supportConstantColor = exports.supportConstantColor = "aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen";
var supportConstantFonts = exports.supportConstantFonts = "arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace";
var numRe = exports.numRe = "\\-?(?:(?:[0-9]+(?:\\.[0-9]+)?)|(?:\\.[0-9]+))";
var pseudoElements = exports.pseudoElements = "(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b";
var pseudoClasses = exports.pseudoClasses = "(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b";
var CssHighlightRules = function () {
var keywordMapper = this.createKeywordMapper({
"support.function": supportFunction,
"support.constant": supportConstant,
"support.type": supportType,
"support.constant.color": supportConstantColor,
"support.constant.fonts": supportConstantFonts
}, "text", true);
this.$rules = {
"start": [{
include: ["strings", "url", "comments"]
}, {
token: "paren.lparen",
regex: "\\{",
next: "ruleset"
}, {
token: "paren.rparen",
regex: "\\}"
}, {
token: "string",
regex: "@(?!viewport)",
next: "media"
}, {
token: "keyword",
regex: "#[a-z0-9-_]+"
}, {
token: "keyword",
regex: "%"
}, {
token: "variable",
regex: "\\.[a-z0-9-_]+"
}, {
token: "string",
regex: ":[a-z0-9-_]+"
}, {
token: "constant.numeric",
regex: numRe
}, {
token: "constant",
regex: "[a-z0-9-_]+"
}, {
caseInsensitive: true
}],
"media": [{
include: ["strings", "url", "comments"]
}, {
token: "paren.lparen",
regex: "\\{",
next: "start"
}, {
token: "paren.rparen",
regex: "\\}",
next: "start"
}, {
token: "string",
regex: ";",
next: "start"
}, {
token: "keyword",
regex: "(?:media|supports|document|charset|import|namespace|media|supports|document"
+ "|page|font|keyframes|viewport|counter-style|font-feature-values"
+ "|swash|ornaments|annotation|stylistic|styleset|character-variant)"
}],
"comments": [{
token: "comment", // multi line comment
regex: "\\/\\*",
push: [{
token: "comment",
regex: "\\*\\/",
next: "pop"
}, {
defaultToken: "comment"
}]
}],
"ruleset": [{
regex: "-(webkit|ms|moz|o)-",
token: "text"
}, {
token: "punctuation.operator",
regex: "[:;]"
}, {
token: "paren.rparen",
regex: "\\}",
next: "start"
}, {
include: ["strings", "url", "comments"]
}, {
token: ["constant.numeric", "keyword"],
regex: "(" + numRe + ")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vmax|vmin|vm|vw|%)"
}, {
token: "constant.numeric",
regex: numRe
}, {
token: "constant.numeric", // hex6 color
regex: "#[a-f0-9]{6}"
}, {
token: "constant.numeric", // hex3 color
regex: "#[a-f0-9]{3}"
}, {
token: ["punctuation", "entity.other.attribute-name.pseudo-element.css"],
regex: pseudoElements
}, {
token: ["punctuation", "entity.other.attribute-name.pseudo-class.css"],
regex: pseudoClasses
}, {
include: "url"
}, {
token: keywordMapper,
regex: "\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"
}, {
token: "paren.lparen",
regex: "\\{"
}, {
caseInsensitive: true
}],
url: [{
token: "support.function",
regex: "(?:url(:?-prefix)?|domain|regexp)\\(",
push: [{
token: "support.function",
regex: "\\)",
next: "pop"
}, {
defaultToken: "string"
}]
}],
strings: [{
token: "string.start",
regex: "'",
push: [{
token: "string.end",
regex: "'|$",
next: "pop"
}, {
include: "escapes"
}, {
token: "constant.language.escape",
regex: /\\$/,
consumeLineEnd: true
}, {
defaultToken: "string"
}]
}, {
token: "string.start",
regex: '"',
push: [{
token: "string.end",
regex: '"|$',
next: "pop"
}, {
include: "escapes"
}, {
token: "constant.language.escape",
regex: /\\$/,
consumeLineEnd: true
}, {
defaultToken: "string"
}]
}],
escapes: [{
token: "constant.language.escape",
regex: /\\([a-fA-F\d]{1,6}|[^a-fA-F\d])/
}]
};
this.normalizeRules();
};
oop.inherits(CssHighlightRules, TextHighlightRules);
exports.CssHighlightRules = CssHighlightRules;
});
ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(require, exports, module){"use strict";
var Range = require("../range").Range;
var MatchingBraceOutdent = function () { };
(function () {
this.checkOutdent = function (line, input) {
if (!/^\s+$/.test(line))
return false;
return /^\s*\}/.test(input);
};
this.autoOutdent = function (doc, row) {
var line = doc.getLine(row);
var match = line.match(/^(\s*\})/);
if (!match)
return 0;
var column = match[1].length;
var openBracePos = doc.findMatchingBracket({ row: row, column: column });
if (!openBracePos || openBracePos.row == row)
return 0;
var indent = this.$getIndent(doc.getLine(openBracePos.row));
doc.replace(new Range(row, 0, row, column - 1), indent);
};
this.$getIndent = function (line) {
return line.match(/^\s*/)[0];
};
}).call(MatchingBraceOutdent.prototype);
exports.MatchingBraceOutdent = MatchingBraceOutdent;
});
ace.define("ace/mode/css_completions",["require","exports","module"], function(require, exports, module){"use strict";
var propertyMap = {
"background": { "#$0": 1 },
"background-color": { "#$0": 1, "transparent": 1, "fixed": 1 },
"background-image": { "url('/$0')": 1 },
"background-repeat": { "repeat": 1, "repeat-x": 1, "repeat-y": 1, "no-repeat": 1, "inherit": 1 },
"background-position": { "bottom": 2, "center": 2, "left": 2, "right": 2, "top": 2, "inherit": 2 },
"background-attachment": { "scroll": 1, "fixed": 1 },
"background-size": { "cover": 1, "contain": 1 },
"background-clip": { "border-box": 1, "padding-box": 1, "content-box": 1 },
"background-origin": { "border-box": 1, "padding-box": 1, "content-box": 1 },
"border": { "solid $0": 1, "dashed $0": 1, "dotted $0": 1, "#$0": 1 },
"border-color": { "#$0": 1 },
"border-style": { "solid": 2, "dashed": 2, "dotted": 2, "double": 2, "groove": 2, "hidden": 2, "inherit": 2, "inset": 2, "none": 2, "outset": 2, "ridged": 2 },
"border-collapse": { "collapse": 1, "separate": 1 },
"bottom": { "px": 1, "em": 1, "%": 1 },
"clear": { "left": 1, "right": 1, "both": 1, "none": 1 },
"color": { "#$0": 1, "rgb(#$00,0,0)": 1 },
"cursor": { "default": 1, "pointer": 1, "move": 1, "text": 1, "wait": 1, "help": 1, "progress": 1, "n-resize": 1, "ne-resize": 1, "e-resize": 1, "se-resize": 1, "s-resize": 1, "sw-resize": 1, "w-resize": 1, "nw-resize": 1 },
"display": { "none": 1, "block": 1, "inline": 1, "inline-block": 1, "table-cell": 1 },
"empty-cells": { "show": 1, "hide": 1 },
"float": { "left": 1, "right": 1, "none": 1 },
"font-family": { "Arial": 2, "Comic Sans MS": 2, "Consolas": 2, "Courier New": 2, "Courier": 2, "Georgia": 2, "Monospace": 2, "Sans-Serif": 2, "Segoe UI": 2, "Tahoma": 2, "Times New Roman": 2, "Trebuchet MS": 2, "Verdana": 1 },
"font-size": { "px": 1, "em": 1, "%": 1 },
"font-weight": { "bold": 1, "normal": 1 },
"font-style": { "italic": 1, "normal": 1 },
"font-variant": { "normal": 1, "small-caps": 1 },
"height": { "px": 1, "em": 1, "%": 1 },
"left": { "px": 1, "em": 1, "%": 1 },
"letter-spacing": { "normal": 1 },
"line-height": { "normal": 1 },
"list-style-type": { "none": 1, "disc": 1, "circle": 1, "square": 1, "decimal": 1, "decimal-leading-zero": 1, "lower-roman": 1, "upper-roman": 1, "lower-greek": 1, "lower-latin": 1, "upper-latin": 1, "georgian": 1, "lower-alpha": 1, "upper-alpha": 1 },
"margin": { "px": 1, "em": 1, "%": 1 },
"margin-right": { "px": 1, "em": 1, "%": 1 },
"margin-left": { "px": 1, "em": 1, "%": 1 },
"margin-top": { "px": 1, "em": 1, "%": 1 },
"margin-bottom": { "px": 1, "em": 1, "%": 1 },
"max-height": { "px": 1, "em": 1, "%": 1 },
"max-width": { "px": 1, "em": 1, "%": 1 },
"min-height": { "px": 1, "em": 1, "%": 1 },
"min-width": { "px": 1, "em": 1, "%": 1 },
"overflow": { "hidden": 1, "visible": 1, "auto": 1, "scroll": 1 },
"overflow-x": { "hidden": 1, "visible": 1, "auto": 1, "scroll": 1 },
"overflow-y": { "hidden": 1, "visible": 1, "auto": 1, "scroll": 1 },
"padding": { "px": 1, "em": 1, "%": 1 },
"padding-top": { "px": 1, "em": 1, "%": 1 },
"padding-right": { "px": 1, "em": 1, "%": 1 },
"padding-bottom": { "px": 1, "em": 1, "%": 1 },
"padding-left": { "px": 1, "em": 1, "%": 1 },
"page-break-after": { "auto": 1, "always": 1, "avoid": 1, "left": 1, "right": 1 },
"page-break-before": { "auto": 1, "always": 1, "avoid": 1, "left": 1, "right": 1 },
"position": { "absolute": 1, "relative": 1, "fixed": 1, "static": 1 },
"right": { "px": 1, "em": 1, "%": 1 },
"table-layout": { "fixed": 1, "auto": 1 },
"text-decoration": { "none": 1, "underline": 1, "line-through": 1, "blink": 1 },
"text-align": { "left": 1, "right": 1, "center": 1, "justify": 1 },
"text-transform": { "capitalize": 1, "uppercase": 1, "lowercase": 1, "none": 1 },
"top": { "px": 1, "em": 1, "%": 1 },
"vertical-align": { "top": 1, "bottom": 1 },
"visibility": { "hidden": 1, "visible": 1 },
"white-space": { "nowrap": 1, "normal": 1, "pre": 1, "pre-line": 1, "pre-wrap": 1 },
"width": { "px": 1, "em": 1, "%": 1 },
"word-spacing": { "normal": 1 },
"filter": { "alpha(opacity=$0100)": 1 },
"text-shadow": { "$02px 2px 2px #777": 1 },
"text-overflow": { "ellipsis-word": 1, "clip": 1, "ellipsis": 1 },
"-moz-border-radius": 1,
"-moz-border-radius-topright": 1,
"-moz-border-radius-bottomright": 1,
"-moz-border-radius-topleft": 1,
"-moz-border-radius-bottomleft": 1,
"-webkit-border-radius": 1,
"-webkit-border-top-right-radius": 1,
"-webkit-border-top-left-radius": 1,
"-webkit-border-bottom-right-radius": 1,
"-webkit-border-bottom-left-radius": 1,
"-moz-box-shadow": 1,
"-webkit-box-shadow": 1,
"transform": { "rotate($00deg)": 1, "skew($00deg)": 1 },
"-moz-transform": { "rotate($00deg)": 1, "skew($00deg)": 1 },
"-webkit-transform": { "rotate($00deg)": 1, "skew($00deg)": 1 }
};
var CssCompletions = function () {
};
(function () {
this.completionsDefined = false;
this.defineCompletions = function () {
if (document) {
var style = document.createElement('c').style;
for (var i in style) {
if (typeof style[i] !== 'string')
continue;
var name = i.replace(/[A-Z]/g, function (x) {
return '-' + x.toLowerCase();
});
if (!propertyMap.hasOwnProperty(name))
propertyMap[name] = 1;
}
}
this.completionsDefined = true;
};
this.getCompletions = function (state, session, pos, prefix) {
if (!this.completionsDefined) {
this.defineCompletions();
}
if (state === 'ruleset' || session.$mode.$id == "ace/mode/scss") {
var line = session.getLine(pos.row).substr(0, pos.column);
var inParens = /\([^)]*$/.test(line);
if (inParens) {
line = line.substr(line.lastIndexOf('(') + 1);
}
if (/:[^;]+$/.test(line)) {
/([\w\-]+):[^:]*$/.test(line);
return this.getPropertyValueCompletions(state, session, pos, prefix);
}
else {
return this.getPropertyCompletions(state, session, pos, prefix, inParens);
}
}
return [];
};
this.getPropertyCompletions = function (state, session, pos, prefix, skipSemicolon) {
skipSemicolon = skipSemicolon || false;
var properties = Object.keys(propertyMap);
return properties.map(function (property) {
return {
caption: property,
snippet: property + ': $0' + (skipSemicolon ? '' : ';'),
meta: "property",
score: 1000000
};
});
};
this.getPropertyValueCompletions = function (state, session, pos, prefix) {
var line = session.getLine(pos.row).substr(0, pos.column);
var property = (/([\w\-]+):[^:]*$/.exec(line) || {})[1];
if (!property)
return [];
var values = [];
if (property in propertyMap && typeof propertyMap[property] === "object") {
values = Object.keys(propertyMap[property]);
}
return values.map(function (value) {
return {
caption: value,
snippet: value,
meta: "property value",
score: 1000000
};
});
};
}).call(CssCompletions.prototype);
exports.CssCompletions = CssCompletions;
});
ace.define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"], function(require, exports, module){"use strict";
var oop = require("../../lib/oop");
var Behaviour = require("../behaviour").Behaviour;
var CstyleBehaviour = require("./cstyle").CstyleBehaviour;
var TokenIterator = require("../../token_iterator").TokenIterator;
var CssBehaviour = function () {
this.inherit(CstyleBehaviour);
this.add("colon", "insertion", function (state, action, editor, session, text) {
if (text === ':' && editor.selection.isEmpty()) {
var cursor = editor.getCursorPosition();
var iterator = new TokenIterator(session, cursor.row, cursor.column);
var token = iterator.getCurrentToken();
if (token && token.value.match(/\s+/)) {
token = iterator.stepBackward();
}
if (token && token.type === 'support.type') {
var line = session.doc.getLine(cursor.row);
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar === ':') {
return {
text: '',
selection: [1, 1]
};
}
if (/^(\s+[^;]|\s*$)/.test(line.substring(cursor.column))) {
return {
text: ':;',
selection: [1, 1]
};
}
}
}
});
this.add("colon", "deletion", function (state, action, editor, session, range) {
var selected = session.doc.getTextRange(range);
if (!range.isMultiLine() && selected === ':') {
var cursor = editor.getCursorPosition();
var iterator = new TokenIterator(session, cursor.row, cursor.column);
var token = iterator.getCurrentToken();
if (token && token.value.match(/\s+/)) {
token = iterator.stepBackward();
}
if (token && token.type === 'support.type') {
var line = session.doc.getLine(range.start.row);
var rightChar = line.substring(range.end.column, range.end.column + 1);
if (rightChar === ';') {
range.end.column++;
return range;
}
}
}
});
this.add("semicolon", "insertion", function (state, action, editor, session, text) {
if (text === ';' && editor.selection.isEmpty()) {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar === ';') {
return {
text: '',
selection: [1, 1]
};
}
}
});
this.add("!important", "insertion", function (state, action, editor, session, text) {
if (text === '!' && editor.selection.isEmpty()) {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
if (/^\s*(;|}|$)/.test(line.substring(cursor.column))) {
return {
text: '!important',
selection: [10, 10]
};
}
}
});
};
oop.inherits(CssBehaviour, CstyleBehaviour);
exports.CssBehaviour = CssBehaviour;
});
ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(require, exports, module){"use strict";
var oop = require("../../lib/oop");
var Range = require("../../range").Range;
var BaseFoldMode = require("./fold_mode").FoldMode;
var FoldMode = exports.FoldMode = function (commentRegex) {
if (commentRegex) {
this.foldingStartMarker = new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start));
this.foldingStopMarker = new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end));
}
};
oop.inherits(FoldMode, BaseFoldMode);
(function () {
this.foldingStartMarker = /([\{\[\(])[^\}\]\)]*$|^\s*(\/\*)/;
this.foldingStopMarker = /^[^\[\{\(]*([\}\]\)])|^[\s\*]*(\*\/)/;
this.singleLineBlockCommentRe = /^\s*(\/\*).*\*\/\s*$/;
this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/;
this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/;
this._getFoldWidgetBase = this.getFoldWidget;
this.getFoldWidget = function (session, foldStyle, row) {
var line = session.getLine(row);
if (this.singleLineBlockCommentRe.test(line)) {
if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line))
return "";
}
var fw = this._getFoldWidgetBase(session, foldStyle, row);
if (!fw && this.startRegionRe.test(line))
return "start"; // lineCommentRegionStart
return fw;
};
this.getFoldWidgetRange = function (session, foldStyle, row, forceMultiline) {
var line = session.getLine(row);
if (this.startRegionRe.test(line))
return this.getCommentRegionBlock(session, line, row);
var match = line.match(this.foldingStartMarker);
if (match) {
var i = match.index;
if (match[1])
return this.openingBracketBlock(session, match[1], row, i);
var range = session.getCommentFoldRange(row, i + match[0].length, 1);
if (range && !range.isMultiLine()) {
if (forceMultiline) {
range = this.getSectionRange(session, row);
}
else if (foldStyle != "all")
range = null;
}
return range;
}
if (foldStyle === "markbegin")
return;
var match = line.match(this.foldingStopMarker);
if (match) {
var i = match.index + match[0].length;
if (match[1])
return this.closingBracketBlock(session, match[1], row, i);
return session.getCommentFoldRange(row, i, -1);
}
};
this.getSectionRange = function (session, row) {
var line = session.getLine(row);
var startIndent = line.search(/\S/);
var startRow = row;
var startColumn = line.length;
row = row + 1;
var endRow = row;
var maxRow = session.getLength();
while (++row < maxRow) {
line = session.getLine(row);
var indent = line.search(/\S/);
if (indent === -1)
continue;
if (startIndent > indent)
break;
var subRange = this.getFoldWidgetRange(session, "all", row);
if (subRange) {
if (subRange.start.row <= startRow) {
break;
}
else if (subRange.isMultiLine()) {
row = subRange.end.row;
}
else if (startIndent == indent) {
break;
}
}
endRow = row;
}
return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
};
this.getCommentRegionBlock = function (session, line, row) {
var startColumn = line.search(/\s*$/);
var maxRow = session.getLength();
var startRow = row;
var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
var depth = 1;
while (++row < maxRow) {
line = session.getLine(row);
var m = re.exec(line);
if (!m)
continue;
if (m[1])
depth--;
else
depth++;
if (!depth)
break;
}
var endRow = row;
if (endRow > startRow) {
return new Range(startRow, startColumn, endRow, line.length);
}
};
}).call(FoldMode.prototype);
});
ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"], function(require, exports, module){"use strict";
var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var CssHighlightRules = require("./css_highlight_rules").CssHighlightRules;
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
var WorkerClient = require("../worker/worker_client").WorkerClient;
var CssCompletions = require("./css_completions").CssCompletions;
var CssBehaviour = require("./behaviour/css").CssBehaviour;
var CStyleFoldMode = require("./folding/cstyle").FoldMode;
var Mode = function () {
this.HighlightRules = CssHighlightRules;
this.$outdent = new MatchingBraceOutdent();
this.$behaviour = new CssBehaviour();
this.$completer = new CssCompletions();
this.foldingRules = new CStyleFoldMode();
};
oop.inherits(Mode, TextMode);
(function () {
this.foldingRules = "cStyle";
this.blockComment = { start: "/*", end: "*/" };
this.getNextLineIndent = function (state, line, tab) {
var indent = this.$getIndent(line);
var tokens = this.getTokenizer().getLineTokens(line, state).tokens;
if (tokens.length && tokens[tokens.length - 1].type == "comment") {
return indent;
}
var match = line.match(/^.*\{\s*$/);
if (match) {
indent += tab;
}
return indent;
};
this.checkOutdent = function (state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
this.autoOutdent = function (state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
this.getCompletions = function (state, session, pos, prefix) {
return this.$completer.getCompletions(state, session, pos, prefix);
};
this.createWorker = function (session) {
var worker = new WorkerClient(["ace"], "ace/mode/css_worker", "Worker");
worker.attachToDocument(session.getDocument());
worker.on("annotate", function (e) {
session.setAnnotations(e.data);
});
worker.on("terminate", function () {
session.clearAnnotations();
});
return worker;
};
this.$id = "ace/mode/css";
this.snippetFileId = "ace/snippets/css";
}).call(Mode.prototype);
exports.Mode = Mode;
}); (function() {
ace.require(["ace/mode/css"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+67
View File
@@ -0,0 +1,67 @@
function getCookie(cname) {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return true;
}
}
return false;
}
function displayCookieBanner(link) {
const div = document.createElement('div');
const h4 = document.createElement('h4');
const p = document.createElement('p');
const buttonJa = document.createElement('button');
const buttonNein = document.createElement('button');
div.classList.add('cookieBanner');
h4.classList.add('cookieBanner-title');
p.classList.add('cookieBanner-text');
buttonJa.classList.add('cookieBanner-btn', 'cookieBanner-btn-primary');
buttonNein.classList.add('cookieBanner-btn', 'cookieBanner-btn-secondary');
h4.innerHTML = "Cookie Hinweis";
p.innerHTML = "Die Seite, zu welcher dieser Link führt, nutzt einen technisch notwendigen Cookie. Es werden keine Cookies von Drittanbietern verwendet.";
div.append(h4, p);
const buttonsDiv = document.createElement('div');
buttonsDiv.classList.add('cookieBanner-buttons');
buttonJa.innerHTML = "Fortfahren";
buttonNein.innerHTML = "Abbrechen";
buttonJa.addEventListener('click', (el) => {
window.location.assign(link);
});
buttonNein.addEventListener('click', (el) => {
deleteCookieBanner();
});
buttonsDiv.append(buttonNein, buttonJa);
div.append(buttonsDiv);
$('body').append(div);
setTimeout(() => {
div.classList.add('show');
}, 10);
}
function deleteCookieBanner() {
const $div = $('.cookieBanner');
if ($div.length === 0) { return; }
$div.removeClass('show');
setTimeout(() => {
$div.remove();
}, 400);
}
+185
View File
@@ -0,0 +1,185 @@
let lastMsgTime = 0;
let lastMsgContent = '';
const MSG_COOLDOWN = 500;
function fadeOutDiv(div) {
div.removeClass('show');
setTimeout(() => div.remove(), 1500);
}
let $msgDiv = $('.msgDiv');
jQuery(document).ready(function ($) {
if ($msgDiv.length > 1) {
$msgDiv.not(':first').remove();
$msgDiv = $msgDiv.first();
}
if ($msgDiv.length === 0) {
$msgDiv = $('<div class="msgDiv"></div>');
$('body').append($msgDiv);
}
});
const colors = ["rgb(255, 0, 0)", "rgb(0, 255, 0)", "#ff9d00"];
function displayMsg(type, msg) {
const now = Date.now();
if ($msgDiv.length === 0) {
$msgDiv = $('.msgDiv');
}
if (now - lastMsgTime < MSG_COOLDOWN && lastMsgContent === msg) {
return; // ignore spam
}
lastMsgTime = now;
lastMsgContent = msg;
if (type !== 0 && type !== 1 && type !== 2) return;
const d = new Date();
console.group("MsgDivLog:");
console.log("Type:", type);
console.log("Nachricht:", msg);
console.log("Timestamp:", d);
console.groupEnd();
const $div = $('<div class="msgBox"></div>')
.css({ 'border-color': colors[type] })
.text(msg);
$msgDiv.append($div);
setTimeout(() => $div.addClass('show'), 50);
setTimeout(() => fadeOutDiv($div), 5000);
}
function displayInfo(title = '', msg = null) {
// Created clean layout references
const $heading = $('<h3>', { class: 'confirmImportantHeading', text: title || 'Information' });
const $text = $('<p>', { class: 'confirmImportantText', text: msg || 'Diese Aktion ist unumkehrbar.' });
const $submitBtn = $('<button>', { class: 'confirmImportantConfrimBtn', text: "OK" });
const $btnDiv = $('<div>', { class: 'confirmImportantBtnDiv' }).append($submitBtn);
const $textDiv = $('<div>', { class: 'confirmImportantTextDiv' }).append($heading, $text);
const $box = $('<div>', { class: 'confirmImportantBox' }).append($textDiv, $btnDiv);
const $displayBg = $('<div>', { class: 'confirmImportantBg' }).append($box);
$('body').append($displayBg);
const closeModal = () => {
$(document).off('keydown', handleEscape);
$displayBg.remove();
};
const handleEscape = function (e) {
if (e.key === 'Escape' || e.key === 'Esc') {
closeModal(false);
}
};
$(document).on('keydown', handleEscape);
$submitBtn.on('click', function () {
closeModal();
});
}
function displayConfirm(msg = null) {
return new Promise((resolve) => {
// Created clean layout references
const $heading = $('<h3>', { class: 'confirmImportantHeading', text: 'Bitte bestätige diese Aktion' });
const $text = $('<p>', { class: 'confirmImportantText', text: msg || 'Diese Aktion ist unumkehrbar.' });
const $submitBtn = $('<button>', { class: 'confirmImportantConfrimBtn', text: "Bestätigen" });
const $cancelBtn = $('<button>', { class: 'confirmImportantCancelBtn', text: "Abbrechen" });
const $btnDiv = $('<div>', { class: 'confirmImportantBtnDiv' }).append($cancelBtn, $submitBtn);
const $textDiv = $('<div>', { class: 'confirmImportantTextDiv' }).append($heading, $text);
const $box = $('<div>', { class: 'confirmImportantBox' }).append($textDiv, $btnDiv);
const $displayBg = $('<div>', { class: 'confirmImportantBg' }).append($box);
$('body').append($displayBg);
const closeModal = (result) => {
$(document).off('keydown', handleEscape);
$displayBg.remove();
resolve(result);
};
const handleEscape = function (e) {
if (e.key === 'Escape' || e.key === 'Esc') {
closeModal(false);
}
};
$(document).on('keydown', handleEscape);
$submitBtn.on('click', function () {
closeModal(true);
});
$cancelBtn.on('click', function () {
closeModal(false);
});
});
}
function displayConfirmImportant(msg = null, deleteString = "LOESCHEN") {
return new Promise((resolve) => {
const trimmedString = deleteString.trim();
// Created clean layout references
const $heading = $('<h3>', { class: 'confirmImportantHeading', text: 'Bitte bestätige diese Aktion' });
const $text = $('<p>', { class: 'confirmImportantText', text: msg || 'Diese Aktion ist unumkehrbar.' });
const $label = $('<label>', { class: 'confirmImportantLabel', for: 'confirmImportantInput', text: `Bitte geben Sie im untenstehenden Feld "${deleteString}" ein, um fortzufahren` });
const $input = $('<input>', { class: 'confirmImportantInput', id: 'confirmImportantInput', type: "text", placeholder: `Hier "${deleteString}" eingeben` });
const $inputDiv = $('<div>', { class: 'confirmImportantInputDiv' }).append($label, $input);
const $submitBtn = $('<button>', { class: 'confirmImportantConfrimBtn notValidBtn', text: "Bestätigen" });
const $cancelBtn = $('<button>', { class: 'confirmImportantCancelBtn', text: "Abbrechen" });
const $btnDiv = $('<div>', { class: 'confirmImportantBtnDiv' }).append($cancelBtn, $submitBtn);
const $textDiv = $('<div>', { class: 'confirmImportantTextDiv' }).append($heading, $text);
const $box = $('<div>', { class: 'confirmImportantBox' }).append($textDiv, $inputDiv, $btnDiv);
const $displayBg = $('<div>', { class: 'confirmImportantBg' }).append($box);
$('body').append($displayBg);
const closeModal = (result) => {
$(document).off('keydown', handleEscape);
$displayBg.remove();
resolve(result);
};
const handleEscape = function (e) {
if (e.key === 'Escape' || e.key === 'Esc') {
closeModal(false);
}
};
$(document).on('keydown', handleEscape);
$input.on('input', function () {
const matches = $(this).val().trim() === trimmedString;
$submitBtn.toggleClass('notValidBtn', !matches);
$(this).toggleClass('ok', matches).toggleClass('notOk', !matches);
});
$submitBtn.on('click', function () {
if ($input.val().trim() !== trimmedString || $submitBtn.hasClass('notValidBtn')) return;
closeModal(true);
});
$cancelBtn.on('click', function () {
closeModal(false);
});
});
}
+69
View File
@@ -0,0 +1,69 @@
$(document).on('click', '.selectTrigger', function (e) {
e.stopPropagation();
$(this).closest('.customSelect').toggleClass('open');
});
$(document).on('click', '.selectOptions li', function (e) {
e.stopPropagation();
const $item = $(this);
const $container = $item.closest('.customSelect');
const val = $item.data('value');
const text = $item.text();
const $input = $container.find('.selectValue');
const $label = $container.find('.selectLabel');
const $allOptions = $container.find('.selectOptions li');
$allOptions.removeClass('selected');
$item.addClass('selected');
$input.val(val);
$label.text(text);
$input.trigger('change');
$label.removeClass('placeholder');
$container.removeClass('open');
});
/* ── Multi-select trigger ─────────────────── */
$(document).on('click', '.selectTriggerBulk', function (e) {
e.stopPropagation();
$(this).closest('.customSelect').toggleClass('open');
});
$(document).on('click', '.selectOptionsBulk li', function (e) {
e.stopPropagation();
const $item = $(this);
const $container = $item.closest('.customSelect');
const val = $item.data('value');
const text = $item.text();
const $label = $container.find('.selectLabel');
const $input = $container.find('.selectValue');
let inputArr = $input.val() ? JSON.parse($input.val()) : [];
let containerArr = Array.isArray($container.data('value')) ? $container.data('value') : [];
if ($item.hasClass('selected')) {
$item.removeClass('selected');
inputArr = inputArr.filter(i => i !== val);
containerArr = containerArr.filter(i => i !== text);
} else {
$item.addClass('selected');
inputArr.push(val);
containerArr.push(text);
}
$container.data('value', containerArr);
$input.val(JSON.stringify(inputArr));
$input.trigger('change');
if (containerArr.length > 0) {
$label.removeClass('placeholder').text(containerArr.join(', '));
} else {
$label.addClass('placeholder').text('Bitte auswählen…');
}
});
$(document).on('click', () => $('.customSelect').removeClass('open'));
+1
View File
@@ -0,0 +1 @@
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).de={})}(this,function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},t={weekdays:{shorthand:["So","Mo","Di","Mi","Do","Fr","Sa"],longhand:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"]},months:{shorthand:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],longhand:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"]},firstDayOfWeek:1,weekAbbreviation:"KW",rangeSeparator:" bis ",scrollTitle:"Zum Ändern scrollen",toggleTitle:"Zum Umschalten klicken",time_24hr:!0};n.l10ns.de=t;n=n.l10ns;e.German=t,e.default=n,Object.defineProperty(e,"__esModule",{value:!0})});
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
!function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports):"function"==typeof define&&define.amd?define(["exports"],o):o((e="undefined"!=typeof globalThis?globalThis:e||self).it={})}(this,function(e){"use strict";var o="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},n={weekdays:{shorthand:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],longhand:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"]},months:{shorthand:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],longhand:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"]},firstDayOfWeek:1,ordinal:function(){return"°"},rangeSeparator:" al ",weekAbbreviation:"Se",scrollTitle:"Scrolla per aumentare",toggleTitle:"Clicca per cambiare",time_24hr:!0};o.l10ns.it=n;o=o.l10ns;e.Italian=n,e.default=o,Object.defineProperty(e,"__esModule",{value:!0})});
+1
View File
@@ -0,0 +1 @@
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).de={})}(this,function(e){"use strict";var n="undefined"!=typeof window&&void 0!==window.flatpickr?window.flatpickr:{l10ns:{}},t={weekdays:{shorthand:["So","Mo","Di","Mi","Do","Fr","Sa"],longhand:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"]},months:{shorthand:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],longhand:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"]},firstDayOfWeek:1,weekAbbreviation:"KW",rangeSeparator:" bis ",scrollTitle:"Zum Ändern scrollen",toggleTitle:"Zum Umschalten klicken",time_24hr:!0};n.l10ns.de=t;n=n.l10ns;e.German=t,e.default=n,Object.defineProperty(e,"__esModule",{value:!0})});
+24
View File
@@ -0,0 +1,24 @@
jQuery(document).ready(function ($) {
$('.justified').justifiedGallery({
rowHeight: 250,
margins: 5,
lastRow: 'center',
captions: false
}).on('jg.complete', function () {
$('.justified').css('opacity', 1);
const leftArrowSVGString = '<svg aria-hidden="true" class="pswp__icn" viewBox="0 0 100 125" width="100" height="125"><path d="M5,50L50,5l3,3L11,50l42,42l-3,3L5,50z M92,95l3-3L53,50L95,8l-3-3L47,50L92,95z"/></svg>';
const lightbox = new PhotoSwipeLightbox({
arrowPrevSVG: leftArrowSVGString,
arrowNextSVG: leftArrowSVGString,
gallery: '.imgGallery',
children: 'a.imgLink',
pswpModule: PhotoSwipe,
captionContent: (slide) => {
return slide.data.element.dataset.pswpCaption || '';
}
});
lightbox.init();
});
});
File diff suppressed because one or more lines are too long
+11
View File
File diff suppressed because one or more lines are too long
+52
View File
@@ -0,0 +1,52 @@
$(document).ready(function () {
$('.menuLinksDiv>div').hover(function () {
const $this = $(this);
clearTimeout($this.data('hoverTimeout'));
$this.find('.dropdown').stop(true, false).slideDown(200);
$this.addClass('open');
}, function () {
const $this = $(this);
const timeout = setTimeout(function () {
$this.find('.dropdown').stop(true, false).slideUp(200);
$this.removeClass('open');
}, 200); // Small delay to prevent flickering
$this.data('hoverTimeout', timeout);
});
$('.sidebar>div').on('click', function (e) {
if ($(e.target).closest('.dropdown').length) {
return;
}
if (!$(this).hasClass('open')) {
$('.sidebar>div.open').removeClass('open').find('.dropdown').slideUp();
$(this).addClass('open');
$(this).find('.dropdown').slideDown();
} else {
$(this).find('.dropdown').slideUp();
$(this).removeClass('open');
}
});
$(document).on('click', function (e) {
// If sidebar is not active, do nothing
if ($(e.target).closest('.burgerMenuDiv').length) {
$('.burgerMenuDiv').toggleClass('active');
$('.sidebar').toggleClass('active');
return;
}
if (!$('.sidebar').hasClass('active')) {
return;
}
// If click is inside the sidebar or burger menu, do nothing
if (
$(e.target).closest('.sidebar').length ||
$(e.target).closest('.burgerMenuDiv').length
) {
return;
}
// Otherwise, close the sidebar
$('.sidebar').removeClass('active');
$('.burgerMenuDiv').removeClass('active');
});
});
+11
View File
@@ -0,0 +1,11 @@
jQuery(document).ready(function($) {
const lightbox = new PhotoSwipeLightbox({
gallery: '.imgGallery',
children: 'a.imgLink',
pswpModule: PhotoSwipe,
captionContent: (slide) => {
return slide.data.element.dataset.pswpCaption || '';
}
});
lightbox.init();
});
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+12
View File
@@ -0,0 +1,12 @@
/*!
* jQuery UI Touch Punch 0.2.3
*
* Copyright 20112014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
!function (a) { function f(a, b) { if (!(a.originalEvent.touches.length > 1)) { a.preventDefault(); var c = a.originalEvent.changedTouches[0], d = document.createEvent("MouseEvents"); d.initMouseEvent(b, !0, !0, window, 1, c.screenX, c.screenY, c.clientX, c.clientY, !1, !1, !1, !1, 0, null), a.target.dispatchEvent(d) } } if (a.support.touch = "ontouchend" in document, a.support.touch) { var e, b = a.ui.mouse.prototype, c = b._mouseInit, d = b._mouseDestroy; b._touchStart = function (a) { var b = this; !e && b._mouseCapture(a.originalEvent.changedTouches[0]) && (e = !0, b._touchMoved = !1, f(a, "mouseover"), f(a, "mousemove"), f(a, "mousedown")) }, b._touchMove = function (a) { e && (this._touchMoved = !0, f(a, "mousemove")) }, b._touchEnd = function (a) { e && (f(a, "mouseup"), f(a, "mouseout"), this._touchMoved || f(a, "click"), e = !1) }, b._mouseInit = function () { var b = this; b.element.bind({ touchstart: a.proxy(b, "_touchStart"), touchmove: a.proxy(b, "_touchMove"), touchend: a.proxy(b, "_touchEnd") }), c.call(b) }, b._mouseDestroy = function () { var b = this; b.element.unbind({ touchstart: a.proxy(b, "_touchStart"), touchmove: a.proxy(b, "_touchMove"), touchend: a.proxy(b, "_touchEnd") }), d.call(b) } } }(jQuery);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14
View File
@@ -0,0 +1,14 @@
The MIT License (MIT)
---------------------
Copyright (c) 2012 davidshimjs
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+18
View File
@@ -0,0 +1,18 @@
{
"name": "qrcode.js",
"version": "0.0.1",
"homepage": "https://github.com/davidshimjs/qrcodejs",
"authors": [
"Sangmin Shim", "Sangmin Shim <ssm0123@gmail.com> (http://jaguarjs.com)"
],
"description": "Cross-browser QRCode generator for javascript",
"main": "qrcode.js",
"ignore": [
"bower_components",
"node_modules",
"index.html",
"index.svg",
"jquery.min.js",
"qrcode.min.js"
]
}
+614
View File
@@ -0,0 +1,614 @@
/**
* @fileoverview
* - Using the 'QRCode for Javascript library'
* - Fixed dataset of 'QRCode for Javascript library' for support full-spec.
* - this library has no dependencies.
*
* @author davidshimjs
* @see <a href="http://www.d-project.com/" target="_blank">http://www.d-project.com/</a>
* @see <a href="http://jeromeetienne.github.com/jquery-qrcode/" target="_blank">http://jeromeetienne.github.com/jquery-qrcode/</a>
*/
var QRCode;
(function () {
//---------------------------------------------------------------------
// QRCode for JavaScript
//
// Copyright (c) 2009 Kazuhiko Arase
//
// URL: http://www.d-project.com/
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license.php
//
// The word "QR Code" is registered trademark of
// DENSO WAVE INCORPORATED
// http://www.denso-wave.com/qrcode/faqpatent-e.html
//
//---------------------------------------------------------------------
function QR8bitByte(data) {
this.mode = QRMode.MODE_8BIT_BYTE;
this.data = data;
this.parsedData = [];
// Added to support UTF-8 Characters
for (var i = 0, l = this.data.length; i < l; i++) {
var byteArray = [];
var code = this.data.charCodeAt(i);
if (code > 0x10000) {
byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[3] = 0x80 | (code & 0x3F);
} else if (code > 0x800) {
byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[2] = 0x80 | (code & 0x3F);
} else if (code > 0x80) {
byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
byteArray[1] = 0x80 | (code & 0x3F);
} else {
byteArray[0] = code;
}
this.parsedData.push(byteArray);
}
this.parsedData = Array.prototype.concat.apply([], this.parsedData);
if (this.parsedData.length != this.data.length) {
this.parsedData.unshift(191);
this.parsedData.unshift(187);
this.parsedData.unshift(239);
}
}
QR8bitByte.prototype = {
getLength: function (buffer) {
return this.parsedData.length;
},
write: function (buffer) {
for (var i = 0, l = this.parsedData.length; i < l; i++) {
buffer.put(this.parsedData[i], 8);
}
}
};
function QRCodeModel(typeNumber, errorCorrectLevel) {
this.typeNumber = typeNumber;
this.errorCorrectLevel = errorCorrectLevel;
this.modules = null;
this.moduleCount = 0;
this.dataCache = null;
this.dataList = [];
}
QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);}
return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row<this.moduleCount;row++){this.modules[row]=new Array(this.moduleCount);for(var col=0;col<this.moduleCount;col++){this.modules[row][col]=null;}}
this.setupPositionProbePattern(0,0);this.setupPositionProbePattern(this.moduleCount-7,0);this.setupPositionProbePattern(0,this.moduleCount-7);this.setupPositionAdjustPattern();this.setupTimingPattern();this.setupTypeInfo(test,maskPattern);if(this.typeNumber>=7){this.setupTypeNumber(test);}
if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);}
this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}}
return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row<this.modules.length;row++){var y=row*cs;for(var col=0;col<this.modules[row].length;col++){var x=col*cs;var dark=this.modules[row][col];if(dark){qr_mc.beginFill(0,100);qr_mc.moveTo(x,y);qr_mc.lineTo(x+cs,y);qr_mc.lineTo(x+cs,y+cs);qr_mc.lineTo(x,y+cs);qr_mc.endFill();}}}
return qr_mc;},setupTimingPattern:function(){for(var r=8;r<this.moduleCount-8;r++){if(this.modules[r][6]!=null){continue;}
this.modules[r][6]=(r%2==0);}
for(var c=8;c<this.moduleCount-8;c++){if(this.modules[6][c]!=null){continue;}
this.modules[6][c]=(c%2==0);}},setupPositionAdjustPattern:function(){var pos=QRUtil.getPatternPosition(this.typeNumber);for(var i=0;i<pos.length;i++){for(var j=0;j<pos.length;j++){var row=pos[i];var col=pos[j];if(this.modules[row][col]!=null){continue;}
for(var r=-2;r<=2;r++){for(var c=-2;c<=2;c++){if(r==-2||r==2||c==-2||c==2||(r==0&&c==0)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}}}},setupTypeNumber:function(test){var bits=QRUtil.getBCHTypeNumber(this.typeNumber);for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;}
for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}}
for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}}
this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex<data.length){dark=(((data[byteIndex]>>>bitIndex)&1)==1);}
var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;}
this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}}
row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;i<dataList.length;i++){var data=dataList[i];buffer.put(data.mode,4);buffer.put(data.getLength(),QRUtil.getLengthInBits(data.mode,typeNumber));data.write(buffer);}
var totalDataCount=0;for(var i=0;i<rsBlocks.length;i++){totalDataCount+=rsBlocks[i].dataCount;}
if(buffer.getLengthInBits()>totalDataCount*8){throw new Error("code length overflow. ("
+buffer.getLengthInBits()
+">"
+totalDataCount*8
+")");}
if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);}
while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);}
while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;}
buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;}
buffer.put(QRCodeModel.PAD1,8);}
return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r<rsBlocks.length;r++){var dcCount=rsBlocks[r].dataCount;var ecCount=rsBlocks[r].totalCount-dcCount;maxDcCount=Math.max(maxDcCount,dcCount);maxEcCount=Math.max(maxEcCount,ecCount);dcdata[r]=new Array(dcCount);for(var i=0;i<dcdata[r].length;i++){dcdata[r][i]=0xff&buffer.buffer[i+offset];}
offset+=dcCount;var rsPoly=QRUtil.getErrorCorrectPolynomial(ecCount);var rawPoly=new QRPolynomial(dcdata[r],rsPoly.getLength()-1);var modPoly=rawPoly.mod(rsPoly);ecdata[r]=new Array(rsPoly.getLength()-1);for(var i=0;i<ecdata[r].length;i++){var modIndex=i+modPoly.getLength()-ecdata[r].length;ecdata[r][i]=(modIndex>=0)?modPoly.get(modIndex):0;}}
var totalCodeCount=0;for(var i=0;i<rsBlocks.length;i++){totalCodeCount+=rsBlocks[i].totalCount;}
var data=new Array(totalCodeCount);var index=0;for(var i=0;i<maxDcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<dcdata[r].length){data[index++]=dcdata[r][i];}}}
for(var i=0;i<maxEcCount;i++){for(var r=0;r<rsBlocks.length;r++){if(i<ecdata[r].length){data[index++]=ecdata[r][i];}}}
return data;};var QRMode={MODE_NUMBER:1<<0,MODE_ALPHA_NUM:1<<1,MODE_8BIT_BYTE:1<<2,MODE_KANJI:1<<3};var QRErrorCorrectLevel={L:1,M:0,Q:3,H:2};var QRMaskPattern={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var QRUtil={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:(1<<10)|(1<<8)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0),G18:(1<<12)|(1<<11)|(1<<10)|(1<<9)|(1<<8)|(1<<5)|(1<<2)|(1<<0),G15_MASK:(1<<14)|(1<<12)|(1<<10)|(1<<4)|(1<<1),getBCHTypeInfo:function(data){var d=data<<10;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)>=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));}
return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));}
return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;}
return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i<errorCorrectLength;i++){a=a.multiply(new QRPolynomial([1,QRMath.gexp(i)],0));}
return a;},getLengthInBits:function(mode,type){if(1<=type&&type<10){switch(mode){case QRMode.MODE_NUMBER:return 10;case QRMode.MODE_ALPHA_NUM:return 9;case QRMode.MODE_8BIT_BYTE:return 8;case QRMode.MODE_KANJI:return 8;default:throw new Error("mode:"+mode);}}else if(type<27){switch(mode){case QRMode.MODE_NUMBER:return 12;case QRMode.MODE_ALPHA_NUM:return 11;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 10;default:throw new Error("mode:"+mode);}}else if(type<41){switch(mode){case QRMode.MODE_NUMBER:return 14;case QRMode.MODE_ALPHA_NUM:return 13;case QRMode.MODE_8BIT_BYTE:return 16;case QRMode.MODE_KANJI:return 12;default:throw new Error("mode:"+mode);}}else{throw new Error("type:"+type);}},getLostPoint:function(qrCode){var moduleCount=qrCode.getModuleCount();var lostPoint=0;for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount;col++){var sameCount=0;var dark=qrCode.isDark(row,col);for(var r=-1;r<=1;r++){if(row+r<0||moduleCount<=row+r){continue;}
for(var c=-1;c<=1;c++){if(col+c<0||moduleCount<=col+c){continue;}
if(r==0&&c==0){continue;}
if(dark==qrCode.isDark(row+r,col+c)){sameCount++;}}}
if(sameCount>5){lostPoint+=(3+sameCount-5);}}}
for(var row=0;row<moduleCount-1;row++){for(var col=0;col<moduleCount-1;col++){var count=0;if(qrCode.isDark(row,col))count++;if(qrCode.isDark(row+1,col))count++;if(qrCode.isDark(row,col+1))count++;if(qrCode.isDark(row+1,col+1))count++;if(count==0||count==4){lostPoint+=3;}}}
for(var row=0;row<moduleCount;row++){for(var col=0;col<moduleCount-6;col++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row,col+1)&&qrCode.isDark(row,col+2)&&qrCode.isDark(row,col+3)&&qrCode.isDark(row,col+4)&&!qrCode.isDark(row,col+5)&&qrCode.isDark(row,col+6)){lostPoint+=40;}}}
for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount-6;row++){if(qrCode.isDark(row,col)&&!qrCode.isDark(row+1,col)&&qrCode.isDark(row+2,col)&&qrCode.isDark(row+3,col)&&qrCode.isDark(row+4,col)&&!qrCode.isDark(row+5,col)&&qrCode.isDark(row+6,col)){lostPoint+=40;}}}
var darkCount=0;for(var col=0;col<moduleCount;col++){for(var row=0;row<moduleCount;row++){if(qrCode.isDark(row,col)){darkCount++;}}}
var ratio=Math.abs(100*darkCount/moduleCount/moduleCount-50)/5;lostPoint+=ratio*10;return lostPoint;}};var QRMath={glog:function(n){if(n<1){throw new Error("glog("+n+")");}
return QRMath.LOG_TABLE[n];},gexp:function(n){while(n<0){n+=255;}
while(n>=256){n-=255;}
return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<<i;}
for(var i=8;i<256;i++){QRMath.EXP_TABLE[i]=QRMath.EXP_TABLE[i-4]^QRMath.EXP_TABLE[i-5]^QRMath.EXP_TABLE[i-6]^QRMath.EXP_TABLE[i-8];}
for(var i=0;i<255;i++){QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]]=i;}
function QRPolynomial(num,shift){if(num.length==undefined){throw new Error(num.length+"/"+shift);}
var offset=0;while(offset<num.length&&num[offset]==0){offset++;}
this.num=new Array(num.length-offset+shift);for(var i=0;i<num.length-offset;i++){this.num[i]=num[i+offset];}}
QRPolynomial.prototype={get:function(index){return this.num[index];},getLength:function(){return this.num.length;},multiply:function(e){var num=new Array(this.getLength()+e.getLength()-1);for(var i=0;i<this.getLength();i++){for(var j=0;j<e.getLength();j++){num[i+j]^=QRMath.gexp(QRMath.glog(this.get(i))+QRMath.glog(e.get(j)));}}
return new QRPolynomial(num,0);},mod:function(e){if(this.getLength()-e.getLength()<0){return this;}
var ratio=QRMath.glog(this.get(0))-QRMath.glog(e.get(0));var num=new Array(this.getLength());for(var i=0;i<this.getLength();i++){num[i]=this.get(i);}
for(var i=0;i<e.getLength();i++){num[i]^=QRMath.gexp(QRMath.glog(e.get(i))+ratio);}
return new QRPolynomial(num,0).mod(e);}};function QRRSBlock(totalCount,dataCount){this.totalCount=totalCount;this.dataCount=dataCount;}
QRRSBlock.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];QRRSBlock.getRSBlocks=function(typeNumber,errorCorrectLevel){var rsBlock=QRRSBlock.getRsBlockTable(typeNumber,errorCorrectLevel);if(rsBlock==undefined){throw new Error("bad rs block @ typeNumber:"+typeNumber+"/errorCorrectLevel:"+errorCorrectLevel);}
var length=rsBlock.length/3;var list=[];for(var i=0;i<length;i++){var count=rsBlock[i*3+0];var totalCount=rsBlock[i*3+1];var dataCount=rsBlock[i*3+2];for(var j=0;j<count;j++){list.push(new QRRSBlock(totalCount,dataCount));}}
return list;};QRRSBlock.getRsBlockTable=function(typeNumber,errorCorrectLevel){switch(errorCorrectLevel){case QRErrorCorrectLevel.L:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+0];case QRErrorCorrectLevel.M:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+1];case QRErrorCorrectLevel.Q:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+2];case QRErrorCorrectLevel.H:return QRRSBlock.RS_BLOCK_TABLE[(typeNumber-1)*4+3];default:return undefined;}};function QRBitBuffer(){this.buffer=[];this.length=0;}
QRBitBuffer.prototype={get:function(index){var bufIndex=Math.floor(index/8);return((this.buffer[bufIndex]>>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i<length;i++){this.putBit(((num>>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);}
if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));}
this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]];
function _isSupportCanvas() {
return typeof CanvasRenderingContext2D != "undefined";
}
// android 2.x doesn't support Data-URI spec
function _getAndroid() {
var android = false;
var sAgent = navigator.userAgent;
if (/android/i.test(sAgent)) { // android
android = true;
var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i);
if (aMat && aMat[1]) {
android = parseFloat(aMat[1]);
}
}
return android;
}
var svgDrawer = (function() {
var Drawing = function (el, htOption) {
this._el = el;
this._htOption = htOption;
};
Drawing.prototype.draw = function (oQRCode) {
var _htOption = this._htOption;
var _el = this._el;
var nCount = oQRCode.getModuleCount();
var nWidth = Math.floor(_htOption.width / nCount);
var nHeight = Math.floor(_htOption.height / nCount);
this.clear();
function makeSVG(tag, attrs) {
var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
for (var k in attrs)
if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]);
return el;
}
var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight});
svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
_el.appendChild(svg);
svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"}));
svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"}));
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
if (oQRCode.isDark(row, col)) {
var child = makeSVG("use", {"x": String(col), "y": String(row)});
child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template")
svg.appendChild(child);
}
}
}
};
Drawing.prototype.clear = function () {
while (this._el.hasChildNodes())
this._el.removeChild(this._el.lastChild);
};
return Drawing;
})();
var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
// Drawing in DOM by using Table tag
var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () {
var Drawing = function (el, htOption) {
this._el = el;
this._htOption = htOption;
};
/**
* Draw the QRCode
*
* @param {QRCode} oQRCode
*/
Drawing.prototype.draw = function (oQRCode) {
var _htOption = this._htOption;
var _el = this._el;
var nCount = oQRCode.getModuleCount();
var nWidth = Math.floor(_htOption.width / nCount);
var nHeight = Math.floor(_htOption.height / nCount);
var aHTML = ['<table style="border:0;border-collapse:collapse;">'];
for (var row = 0; row < nCount; row++) {
aHTML.push('<tr>');
for (var col = 0; col < nCount; col++) {
aHTML.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:' + nWidth + 'px;height:' + nHeight + 'px;background-color:' + (oQRCode.isDark(row, col) ? _htOption.colorDark : _htOption.colorLight) + ';"></td>');
}
aHTML.push('</tr>');
}
aHTML.push('</table>');
_el.innerHTML = aHTML.join('');
// Fix the margin values as real size.
var elTable = _el.childNodes[0];
var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2;
var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2;
if (nLeftMarginTable > 0 && nTopMarginTable > 0) {
elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px";
}
};
/**
* Clear the QRCode
*/
Drawing.prototype.clear = function () {
this._el.innerHTML = '';
};
return Drawing;
})() : (function () { // Drawing in Canvas
function _onMakeImage() {
this._elImage.src = this._elCanvas.toDataURL("image/png");
this._elImage.style.display = "block";
this._elCanvas.style.display = "none";
}
// Android 2.1 bug workaround
// http://code.google.com/p/android/issues/detail?id=5141
if (this._android && this._android <= 2.1) {
var factor = 1 / window.devicePixelRatio;
var drawImage = CanvasRenderingContext2D.prototype.drawImage;
CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
if (("nodeName" in image) && /img/i.test(image.nodeName)) {
for (var i = arguments.length - 1; i >= 1; i--) {
arguments[i] = arguments[i] * factor;
}
} else if (typeof dw == "undefined") {
arguments[1] *= factor;
arguments[2] *= factor;
arguments[3] *= factor;
arguments[4] *= factor;
}
drawImage.apply(this, arguments);
};
}
/**
* Check whether the user's browser supports Data URI or not
*
* @private
* @param {Function} fSuccess Occurs if it supports Data URI
* @param {Function} fFail Occurs if it doesn't support Data URI
*/
function _safeSetDataURI(fSuccess, fFail) {
var self = this;
self._fFail = fFail;
self._fSuccess = fSuccess;
// Check it just once
if (self._bSupportDataURI === null) {
var el = document.createElement("img");
var fOnError = function() {
self._bSupportDataURI = false;
if (self._fFail) {
self._fFail.call(self);
}
};
var fOnSuccess = function() {
self._bSupportDataURI = true;
if (self._fSuccess) {
self._fSuccess.call(self);
}
};
el.onabort = fOnError;
el.onerror = fOnError;
el.onload = fOnSuccess;
el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data.
return;
} else if (self._bSupportDataURI === true && self._fSuccess) {
self._fSuccess.call(self);
} else if (self._bSupportDataURI === false && self._fFail) {
self._fFail.call(self);
}
};
/**
* Drawing QRCode by using canvas
*
* @constructor
* @param {HTMLElement} el
* @param {Object} htOption QRCode Options
*/
var Drawing = function (el, htOption) {
this._bIsPainted = false;
this._android = _getAndroid();
this._htOption = htOption;
this._elCanvas = document.createElement("canvas");
this._elCanvas.width = htOption.width;
this._elCanvas.height = htOption.height;
el.appendChild(this._elCanvas);
this._el = el;
this._oContext = this._elCanvas.getContext("2d");
this._bIsPainted = false;
this._elImage = document.createElement("img");
this._elImage.alt = "Scan me!";
this._elImage.style.display = "none";
this._el.appendChild(this._elImage);
this._bSupportDataURI = null;
};
/**
* Draw the QRCode
*
* @param {QRCode} oQRCode
*/
Drawing.prototype.draw = function (oQRCode) {
var _elImage = this._elImage;
var _oContext = this._oContext;
var _htOption = this._htOption;
var nCount = oQRCode.getModuleCount();
var nWidth = _htOption.width / nCount;
var nHeight = _htOption.height / nCount;
var nRoundedWidth = Math.round(nWidth);
var nRoundedHeight = Math.round(nHeight);
_elImage.style.display = "none";
this.clear();
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
var bIsDark = oQRCode.isDark(row, col);
var nLeft = col * nWidth;
var nTop = row * nHeight;
_oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
_oContext.lineWidth = 1;
_oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
// 안티 앨리어싱 방지 처리
_oContext.strokeRect(
Math.floor(nLeft) + 0.5,
Math.floor(nTop) + 0.5,
nRoundedWidth,
nRoundedHeight
);
_oContext.strokeRect(
Math.ceil(nLeft) - 0.5,
Math.ceil(nTop) - 0.5,
nRoundedWidth,
nRoundedHeight
);
}
}
this._bIsPainted = true;
};
/**
* Make the image from Canvas if the browser supports Data URI.
*/
Drawing.prototype.makeImage = function () {
if (this._bIsPainted) {
_safeSetDataURI.call(this, _onMakeImage);
}
};
/**
* Return whether the QRCode is painted or not
*
* @return {Boolean}
*/
Drawing.prototype.isPainted = function () {
return this._bIsPainted;
};
/**
* Clear the QRCode
*/
Drawing.prototype.clear = function () {
this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height);
this._bIsPainted = false;
};
/**
* @private
* @param {Number} nNumber
*/
Drawing.prototype.round = function (nNumber) {
if (!nNumber) {
return nNumber;
}
return Math.floor(nNumber * 1000) / 1000;
};
return Drawing;
})();
/**
* Get the type by string length
*
* @private
* @param {String} sText
* @param {Number} nCorrectLevel
* @return {Number} type
*/
function _getTypeNumber(sText, nCorrectLevel) {
var nType = 1;
var length = _getUTF8Length(sText);
for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) {
var nLimit = 0;
switch (nCorrectLevel) {
case QRErrorCorrectLevel.L :
nLimit = QRCodeLimitLength[i][0];
break;
case QRErrorCorrectLevel.M :
nLimit = QRCodeLimitLength[i][1];
break;
case QRErrorCorrectLevel.Q :
nLimit = QRCodeLimitLength[i][2];
break;
case QRErrorCorrectLevel.H :
nLimit = QRCodeLimitLength[i][3];
break;
}
if (length <= nLimit) {
break;
} else {
nType++;
}
}
if (nType > QRCodeLimitLength.length) {
throw new Error("Too long data");
}
return nType;
}
function _getUTF8Length(sText) {
var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a');
return replacedText.length + (replacedText.length != sText ? 3 : 0);
}
/**
* @class QRCode
* @constructor
* @example
* new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie");
*
* @example
* var oQRCode = new QRCode("test", {
* text : "http://naver.com",
* width : 128,
* height : 128
* });
*
* oQRCode.clear(); // Clear the QRCode.
* oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode.
*
* @param {HTMLElement|String} el target element or 'id' attribute of element.
* @param {Object|String} vOption
* @param {String} vOption.text QRCode link data
* @param {Number} [vOption.width=256]
* @param {Number} [vOption.height=256]
* @param {String} [vOption.colorDark="#000000"]
* @param {String} [vOption.colorLight="#ffffff"]
* @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H]
*/
QRCode = function (el, vOption) {
this._htOption = {
width : 256,
height : 256,
typeNumber : 4,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRErrorCorrectLevel.H
};
if (typeof vOption === 'string') {
vOption = {
text : vOption
};
}
// Overwrites options
if (vOption) {
for (var i in vOption) {
this._htOption[i] = vOption[i];
}
}
if (typeof el == "string") {
el = document.getElementById(el);
}
if (this._htOption.useSVG) {
Drawing = svgDrawer;
}
this._android = _getAndroid();
this._el = el;
this._oQRCode = null;
this._oDrawing = new Drawing(this._el, this._htOption);
if (this._htOption.text) {
this.makeCode(this._htOption.text);
}
};
/**
* Make the QRCode
*
* @param {String} sText link data
*/
QRCode.prototype.makeCode = function (sText) {
this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel);
this._oQRCode.addData(sText);
this._oQRCode.make();
this._el.title = sText;
this._oDrawing.draw(this._oQRCode);
this.makeImage();
};
/**
* Make the Image from Canvas element
* - It occurs automatically
* - Android below 3 doesn't support Data-URI spec.
*
* @private
*/
QRCode.prototype.makeImage = function () {
if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) {
this._oDrawing.makeImage();
}
};
/**
* Clear the QRCode
*/
QRCode.prototype.clear = function () {
this._oDrawing.clear();
};
/**
* @name QRCode.CorrectLevel
*/
QRCode.CorrectLevel = QRErrorCorrectLevel;
})();
File diff suppressed because one or more lines are too long
+105
View File
@@ -0,0 +1,105 @@
(function () {
const STORAGE_KEY = 'intern_sidebar_open';
document.addEventListener('DOMContentLoaded', function () {
const toggle = document.getElementById('sidebar-toggle');
const sidebar = document.getElementById('sidebar-nav');
const overlay = document.getElementById('sidebar-overlay');
if (!toggle || !sidebar || !overlay) return;
function openSidebar() {
sidebar.classList.add('open');
overlay.classList.add('open');
toggle.classList.add('open');
localStorage.setItem(STORAGE_KEY, 'true');
}
function closeSidebar() {
sidebar.classList.remove('open');
overlay.classList.remove('open');
toggle.classList.remove('open');
localStorage.setItem(STORAGE_KEY, 'false');
}
function toggleSidebar() {
if (sidebar.classList.contains('open')) {
closeSidebar();
} else {
openSidebar();
}
}
toggle.addEventListener('click', toggleSidebar);
overlay.addEventListener('click', closeSidebar);
// Keyboard shortcut: press 'M' to toggle
document.addEventListener('keydown', function (e) {
if (e.key.toLowerCase() === 'm' && !e.ctrlKey && !e.altKey && !e.metaKey) {
const tag = document.activeElement?.tagName;
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;
toggleSidebar();
}
});
// Restore state
if (localStorage.getItem(STORAGE_KEY) === 'true') {
openSidebar();
}
});
})();
function changeFreigabe(freigabe) {
const params = new URLSearchParams();
params.append('freigabe', freigabe);
params.append('type', siteType);
params.append('csrf_token', window.CSRF_TOKEN);
fetch('/intern/scripts/ajax-update_selected_freigabe.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
})
.then(res => res.json())
.then(data => {
if (data.success) {
window.location.reload(); // reload page to reflect changes
} else {
alert('Error: ' + data.data);
}
})
.catch(err => {
console.error(err);
alert('AJAX request failed');
});
}
$(document).on('click', '.sidebar-nav .selectTriggerSidebar', function (e) {
e.stopPropagation();
$(this).closest('.customSelect').toggleClass('open');
});
$(document).on('click', '.sidebar-nav .selectOptionsSidebar li', function (e) {
e.stopPropagation();
const $item = $(this);
const $container = $item.closest('.customSelect');
const val = $item.data('value');
const text = $item.text();
const $input = $container.find('.selectValue');
const $label = $container.find('.selectLabel');
const $allOptions = $container.find('.selectOptions li');
$allOptions.removeClass('selected');
$item.addClass('selected');
$input.val(val);
$label.text(text);
$container.removeClass('open');
changeFreigabe(val);
});
$(document).on('click', () => $('.sidebar-nav .customSelect').removeClass('open'));
File diff suppressed because it is too large Load Diff
+490
View File
@@ -0,0 +1,490 @@
<?php
require_once __DIR__ . '/../../scripts/rate-limiter/rate-limiter.php';
$limiter = new TokenBucket(
redis: $redis,
capacity: 3,
refillRate: 1,
refillInterval: 5.0
);
$result = $limiter->allow('RATE_LIMITER:TYPE:otlogin:IP:'. $_SERVER['REMOTE_ADDR']);
if (!$result['allowed']) {
http_response_code(429);
include __DIR__ . "/../error-pages/429.html";
exit;
}
use Dotenv\Dotenv;
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
date_default_timezone_set('Europe/Zurich');
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session(true);
$oturl = $_GET['otl'] ?? '';
if (!$oturl) {
http_response_code(404);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$error = '';
class otl {
private function connectToDB() {
global $mysqli, $baseDir;
if (isset($mysqli)) { return $mysqli; }
$_SESSION['access_granted_db_otl'] = true;
$type = 'otl';
// DB
$dbconnection = require $baseDir .'/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
return "DB Error";
}
$_SESSION['access_granted_db_otl'] = false;
return $mysqli;
}
private function pwProcessing() {
global $baseDir;
require $baseDir . '/../composer/vendor/autoload.php';
$envFile = realpath($baseDir . '/../config/.env.pw-encryption-key');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.pw-encryption-key');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
$password = trim($_POST['password1']);
$passwordRep = trim($_POST['password2']);
if ($password === '' || $passwordRep === '') {
return 'Beide Felder müssen ausgefüllt sein';
}
if ($password !== $passwordRep) {
return 'Beide Passwörter müssen identisch sein';
}
$hash = password_hash($password, PASSWORD_ARGON2ID);
$iv_length = openssl_cipher_iv_length('aes-256-cbc');
$iv = random_bytes($iv_length);
$encrypted = openssl_encrypt(
'SET_BY_OTL',
'aes-256-cbc',
$_ENV['PW_ENCRYPTION_KEY'],
0,
$iv
);
$cipher_store = base64_encode($iv . $encrypted);
return ['success' => true, 'hash' => $hash, 'encpw' => $cipher_store];
}
public function logIn(int $id) {
global $baseDir;
$mysqli = $this->connectToDB();
require $baseDir . '/../scripts/db/db-tables.php';
// delete the one-time token
if (!isset($_SESSION['otl_dbid'])) {
return 'Interner Fehler';
}
$dbid = intval($_SESSION['otl_dbid']);
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_einmal_links WHERE id = ?");
$stmt->bind_param("i", $dbid);
if (!$stmt->execute()) {
return "DB Error";
}
$stmt->close();
$sql = "SELECT freigabe FROM $db_tabelle_intern_benutzende WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$freigabe = $row['freigabe'];
$stmt->close();
$mysqli->close();
unset($_SESSION['set_new_password_id_user'], $_SESSION['set_new_password_granted'], $_SESSION['otl_dbid']);
$freigabenArray = json_decode($freigabe, true) ?? [];
$freigabenTypeArray = $freigabenArray['types'] ?? [];
if (count($freigabenTypeArray) > 0) {
$_SESSION = array();
session_destroy();
session_start();
}
foreach ($freigabenTypeArray as $freigabeType){
$_SESSION['access_granted_'.$freigabeType] = true;
$_SESSION['user_id_'.$freigabeType] = $id;
}
var_dump($_SESSION);
if (in_array('wk_leitung', $freigabenTypeArray)) {
header("Location: /intern/wk-leitung/logindata");
exit;
} elseif (in_array('trainer', $freigabenTypeArray)) {
header("Location: /intern/trainer");
exit;
} elseif (in_array('kampfrichter', $freigabenTypeArray)) {
header("Location: /intern/kampfrichter");
exit;
} else {
return 'Dieser Benutzer hat keine Berechtigungen.';
}
}
public function resetPW() {
global $baseDir;
$iduser = intval($_POST['user_id']);
// security: user must have passed one-time-login first
if (empty($_SESSION['set_new_password_id_user']) || empty($_SESSION['set_new_password_granted']) || $_SESSION['set_new_password_id_user'] !== $iduser || $_SESSION['set_new_password_granted'] !== true) {
http_response_code(403);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
$mysqli = $this->connectToDB();
$pwArray = $this->pwProcessing();
if (!isset($pwArray['success']) || !$pwArray['success']) {
return 'Passwort konnte nicht verarbeitet werden';
}
// update password
$updateResult = db_update($mysqli, $db_tabelle_intern_benutzende, ['password_hash' => $pwArray['hash'] ?? '', 'password_cipher' => $pwArray['encpw'] ?? '', 'edited_by' => 'otlogin'], ['id' => $iduser]);
if ($updateResult === false) {
return 'Passwork konnte nicht neu gesetzt werden';
}
// delete the one-time token
if (!isset($_SESSION['otl_dbid'])) {
return 'Interner Fehler';
}
$dbid = intval($_SESSION['otl_dbid']);
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_einmal_links WHERE id = ?");
$stmt->bind_param("i", $dbid);
if (!$stmt->execute()) {
return "DB Error";
}
$stmt->close();
$this->logIn($iduser);
}
public function createUser() {
global $baseDir;
$iduser = intval($_POST['user_id']);
if (empty($_SESSION['set_new_user_id_user']) || empty($_SESSION['set_new_user_granted']) || $_SESSION['set_new_user_id_user'] !== $iduser || $_SESSION['set_new_user_granted'] !== true) {
http_response_code(403);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
$mysqli = $this->connectToDB();
$arrayDB = [];
if (isset($_POST['password1'], $_POST['password2'])) {
$pwArray = $this->pwProcessing();
if (!isset($pwArray['success']) || !$pwArray['success']) {
return 'Passwort konnte nicht verarbeitet werden';
}
$arrayDB[] = ["name" => 'password_hash', "value" => $pwArray['hash']];
$arrayDB[] = ["name" => 'password_cipher', "value" => $pwArray['encpw']];
}
if (isset($_POST['username'])) {
$arrayDB[] = ["name" => 'username', "value" => htmlspecialchars(trim($_POST['username']))];
}
if (isset($_POST['name_person'])) {
$arrayDB[] = ["name" => 'name_person', "value" => htmlspecialchars(trim($_POST['name_person']))];
}
// --- NEW LOGIC TO UTILIZE $arrayDB ---
$updateData = [
'edited_by' => 'otlogin',
'login_active' => 1
];
// Convert the $arrayDB list into a flat associative array
if (!empty($arrayDB)) {
foreach ($arrayDB as $entry) {
$updateData[$entry['name']] = $entry['value'];
}
}
// Execute update using the dynamically built array
$updateResult = db_update(
$mysqli,
$db_tabelle_intern_benutzende,
$updateData,
['id' => $iduser]
);
if ($updateResult === false) {
return 'Nutzer konnte nicht aktualisiert werden';
}
$this->logIn($iduser);
}
}
$pwClass = New otl();
require $baseDir .'/../scripts/db/db-verbindung-script-guest.php';
// fetch one-time login record
$now = date("Y-m-d H:i:s");
$result = db_select(
$guest,
$db_tabelle_einmal_links,
'id, user_id, `type`',
'url = ? AND expires_at >= ?',
[$oturl, $now]
);
if (!$result || count($result) !== 1) {
http_response_code(400);
include $baseDir . "/error-pages/400.html";
exit;
}
$dbid = intval($result[0]['id']);
$iduser = intval($result[0]['user_id']);
if (isset($_POST['password1'], $_POST['password2'], $_POST['setpasswordbtn'], $_POST['user_id']) && $result[0]['type'] === 'pwreset') { $error = $pwClass->resetPW() ?? ''; }
elseif (isset($_POST['setpasswordbtn'], $_POST['user_id']) && $result[0]['type'] === 'create_profile') { $error = $pwClass->createUser() ?? ''; }
// store dbid for later deletion
$_SESSION['otl_dbid'] = $dbid;
if ($result[0]['type'] === 'login') {
$pwClass->logIn($iduser);
}
if ($result[0]['type'] === 'pwreset') {
$userinfo = db_select($guest, $db_tabelle_intern_benutzende, 'username', 'id = ?', [$iduser]);
$username = $userinfo[0]['username'];
if (!$userinfo || count($userinfo) !== 1) {
echo 'Ungültige Benutzerinformationen';
exit;
}
// set session token that grants password reset
$_SESSION['set_new_password_id_user'] = $iduser;
$_SESSION['set_new_password_granted'] = true;
$hasUsername = true;
$hasName = true;
} elseif ($result[0]['type'] === 'create_profile') {
$userinfo = db_select($guest, $db_tabelle_intern_benutzende, 'username, `password_hash`, `name_person`', 'id = ?', [$iduser]);
if (!$userinfo || count($userinfo) !== 1) {
echo 'Ungültige Benutzerinformationen';
exit;
}
$hasPW = $userinfo[0]['password_hash'] !== null;
$hasUsername = $userinfo[0]['username'] !== '';
$username = $userinfo[0]['username'];
$hasName = $userinfo[0]['name_person'] !== '';
unset($userinfo);
// set session token that grants password reset
$_SESSION['set_new_user_id_user'] = $iduser;
$_SESSION['set_new_user_granted'] = true;
}
// fetch user
$guest->close();
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="png" href="/intern/img/icon.png">
<title>Einmal Login</title>
<script src="/intern/js/jquery/jquery-3.7.1.min.js"></script>
<link href="/files/fonts/fonts.css" rel="stylesheet">
<link href="/intern/css/otl.css" rel="stylesheet">
<script src="/intern/js/custom-msg-display.js"></script>
<link rel="stylesheet" href="/intern/css/user.css">
</head>
<body class="otlogin">
<section class="page-secure-login">
<div class="bg-picture-secure-login">
<img src="/intern/img/login/bgOtl.webp">
</div>
<div class="bg-secure-login">
<div class="bg-secure-login-form">
<h1>Einmal-Login</h1>
<p style="font-weight:400; line-height: 1.5; margin-bottom: 50px;">
</p>
<form method="post">
<?php if (!$hasUsername) : ?>
<label for="username">Benutzername</label><br>
<div class="divShowPw">
<input type="text" name="username" id="username" placeholder="Benutzername" required>
</div><br>
<?php endif; ?>
<?php if (!$hasName) : ?>
<label for="name_person">Name</label><br>
<div class="divShowPw">
<input type="text" name="name_person" id="name_person" placeholder="Max Muster" required>
</div><br>
<?php endif; ?>
<label for="password1">Neues Passwort eingeben</label><br>
<div class="divShowPw">
<input type="password" name="password1" id="password1" placeholder="Passwort" required>
<button type="button" class="togglePassword">
<svg class="eyeIcon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
</button>
</div><br>
<label for="password2">Neues Passwort wiederholen</label><br>
<div class="divShowPw" id="lastDivShowPw">
<input type="password" name="password2" id="password2" placeholder="Passwort" required>
<button type="button" class="togglePassword">
<svg class="eyeIcon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
</button>
</div>
<input type="hidden" name="user_id" value="<?= $iduser ?>">
<input type="submit" name="setpasswordbtn" value="Einloggen">
</form>
<?php if ($error !== ''): ?>
<p style="color:red;"><?php echo $error; ?></p>
<?php endif; ?>
</div>
</div>
</section>
<a class="seclog_home_link" href="/"><img src="/intern/img/logo-normal.png" width="64" height="64"></a>
<script>
const toggleButtons = document.querySelectorAll('.togglePassword');
toggleButtons.forEach((el) => {
el.addEventListener('click', () => {
const passwordInput = el.parentElement.querySelector("input");
const eyeIcon = el.querySelector(".eyeIcon");
const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
passwordInput.setAttribute('type', type);
// Swap between eye and eye-with-line
if (type === 'password') {
// Eye (show)
eyeIcon.innerHTML = '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>';
} else {
// Eye with slash (hide)
eyeIcon.innerHTML = '<path d="M17.94 17.94L6.06 6.06"/><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>';
}
});
});
</script>
</body>
</html>
<?php /*<form id="setpasswordform" method="post">
<label for="password">Neues Passwort setzen:</label>
<input type="text" name="password" id="password" placeholder="Neues Passwort hier eingeben">
<input name="setpasswordbtn" type="submit" value="Passwort setzen">
</form>*/
?>
@@ -0,0 +1,38 @@
<?php
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
verify_csrf();
$accessPermission = trim($_POST['accessPermission'] ?? '');
$access = preg_replace("/[\W]/", "", trim($_POST['access'] ?? ''));
$accesstype = preg_replace("/[\W]/", "", trim($_POST['accesstype'] ?? ''));
if ($accessPermission === '' || $access === '' || $accesstype === '') {
echo json_encode(['success' => false, 'message' => 'Parameters not correctly set']);
http_response_code(400);
exit;
}
require $baseDir . "/../scripts/websocket/ws-create-token.php";
if ($accessPermission === "R") {
$token = generateWSReadToken($accesstype, $access);
} elseif ($accessPermission === "W") {
check_multiple_allowed_permissions(['kampfrichter', 'wk_leitung']);
$token = generateWSAdminToken($accesstype, $access);
} else {
http_response_code(403);
exit;
}
$responseBool = $token != null;
echo json_encode(['success' => $responseBool, 'token' => $token]);
@@ -0,0 +1,41 @@
<?php
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
verify_csrf();
$new_value = $_POST['freigabe'] ?? '';
$type = $_POST['type'] ?? 'nan';
$allowedTypes = ['kampfrichter', 'trainer'];
if (in_array($type, $allowedTypes)) {
check_user_permission($type);
} else {
echo json_encode(['success' => false, 'message' => 'no permissions']);
exit;
}
if (!$new_value) {
echo json_encode('Invalid Input');
exit;
}
if ($type === 'kampfrichter'){
$_SESSION['selectedFreigabeIdKampfrichter'] = $new_value;
}
if ($type === 'trainer'){
$_SESSION['selectedFreigabeTrainer'] = $new_value;
}
// ---------- Return JSON ----------
echo json_encode(['success' => true, 'message' => 'SESSION updated']);
exit;
@@ -0,0 +1,4 @@
upload_max_filesize = 50M
post_max_size = 55M
max_execution_time = 120
max_input_time = 120
@@ -0,0 +1,150 @@
<?php
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_multiple_allowed_permissions(['trainer', 'wk_leitung']);
verify_csrf();
$isTrainer = isset($_SESSION['access_granted_trainer']) && $_SESSION['access_granted_trainer'];
$userId = $isTrainer ? intval($_SESSION['user_id_trainer'] ?? 0) : intval($_SESSION['user_id_wk_leitung'] ?? 0);
// Allow large uploads and enough memory for GD processing
ini_set('memory_limit', '256M');
ini_set('max_execution_time', '120');
if (!isset($_FILES['music_file']) || $_FILES['music_file']['error'] !== UPLOAD_ERR_OK) {
echo json_encode([
'success' => false,
'message' => 'Keine Musik' . $_FILES['music_file']['error'] ?? 'NO ERROR KNOWN'
]);
exit;
}
$type = ($isTrainer) ? 'tr' : 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$saveDir = '/files/music/';
$normalDir = $saveDir;
$uploadDir = $baseDir . $saveDir;
$maxLengthMusic = 0;
if ($isTrainer) {
$geraet_id = (int) $_POST['geraetId'] ?? 0;
$geraet_exists = db_get_var($mysqli, "SELECT 1 FROM $db_tabelle_disziplinen WHERE `id` = ?", [$geraet_id]);
if ($geraet_exists === null) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalides Gerät angegeben.']);
exit;
}
$geraet_max_duration_audio_db = db_get_var($mysqli, "SELECT `audiofile_max_duration` FROM $db_tabelle_disziplinen WHERE `id` = ?", [$geraet_id]);
$maxLengthMusic = (int) $geraet_max_duration_audio_db;
}
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$tmpPath = $_FILES['music_file']['tmp_name'];
$originalName = $_FILES['music_file']['name'];
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$allowedExt = ['mp3', 'wav', 'ogg'];
if (!in_array($extension, $allowedExt, true)) {
echo json_encode(['success' => false, 'message' => 'Falsches Format (Endung)']);
http_response_code(422);
exit;
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($tmpPath);
$allowedMime = ['audio/mpeg', 'audio/wav', 'audio/x-wav', 'audio/ogg', 'application/ogg'];
if (!in_array($mimeType, $allowedMime, true)) {
echo json_encode(['success' => false, 'message' => 'Dateiinhalt ist kein gültiges Audio']);
http_response_code(422);
exit;
}
$filename = uniqid('userupload_', true) . '.' . $extension;
$destination = $uploadDir . $filename;
$normalPath = $normalDir . $filename;
if (!move_uploaded_file($tmpPath, $destination)) {
http_response_code(500);
exit;
}
if ($isTrainer && $maxLengthMusic !== null && intval($maxLengthMusic) !== 0) {
require $baseDir . '/../composer/vendor/autoload.php';
$getID3 = new getID3;
$fileInfo = $getID3->analyze($destination);
if (empty($fileInfo['playtime_seconds'])) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'Fehler beim Bestimmen der Musiklänge'
]);
exit;
}
$duration = (float) $fileInfo['playtime_seconds'];
if ($duration > intval($maxLengthMusic)) {
unlink($destination);
echo json_encode([
'success' => false,
'message' => 'Musik zu lange (über ' . intval($maxLengthMusic) . ' Sekunden)'
]);
http_response_code(422);
exit;
}
}
$sql = "INSERT INTO $db_tabelle_audiofiles (`file_name`,`file_path`,`edited_by`) VALUES (?, ?, ?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ssi", $originalName, $normalPath, $userId);
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$id = $mysqli->insert_id;
$stmt->close();
echo json_encode([
'success' => true,
'id' => $id,
'filename' => $originalName,
'filepath' => $normalPath
]);
@@ -0,0 +1,77 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
// ---------- Get and sanitize input ----------
$type = isset($_POST['type']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_POST['type']) : '';
$allowed_types = ['logo','scoring','ctext'];
if (!in_array($type, $allowed_types)) {
echo json_encode(['success' => false, 'message' => 'Invalid type']);
exit;
}
if ($type === 'ctext'){
$ctext = isset($_POST['ctext']) ? $_POST['ctext'] : '';
}
$folder = realpath($baseDir.'/externe-geraete/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'config.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
echo json_encode(['success' => false, 'message' => 'Folder not writable: ' . $folder]);
exit;
}
$jsonString = file_get_contents($filepath);
// decode JSON, fallback to empty array if invalid
$oldjson = json_decode($jsonString, true) ?? [];
$oldjson["type"] = $type;
if ($type === 'ctext'){
$oldjson["ctext"] = $ctext;
}
$jsonData = json_encode($oldjson);
// Write file
if (file_put_contents($filepath, $jsonData) === false) {
echo json_encode([
'success' => false,
'message' => 'Failed to write JSON file: ' . $filepath
]);
exit;
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'JSON type updated',
'disable_start_button' => true
]);
exit;
@@ -0,0 +1,4 @@
upload_max_filesize = 50M
post_max_size = 55M
max_execution_time = 120
max_input_time = 120
@@ -0,0 +1,97 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
$allowedTypes = [
'wkName',
'displayCTextLogo',
'displayCTextWKName',
'displayColourLogo',
'displayTextColourLogo',
'displayColorScoringBg',
'displayColorScoringBgSoft',
'displayColorScoringShadowColor',
'displayColorScoringBorderColor',
'displayColorScoringPanel',
'displayColorScoringPanelSoft',
'displayColorScoringPanelText',
'displayColorScoringPanelTextSoft',
'displayColorScoringPanelTextNoteL',
'displayColorScoringPanelTextNoteR',
'displayIdNoteL',
'displayIdNoteR',
'rechnungenName',
'rechnungenVorname',
'rechnungenStrasse',
'rechnungenHausnummer',
'rechnungenPostleitzahl',
'rechnungenOrt',
'rechnungenIBAN',
'maxLengthMusic',
'linkWebseite',
'rangNote',
'orderBestRang',
'rechnungenPostversand',
'rechnungenPostversandKosten',
'personEinzahl',
'personMehrzahl',
'rankLivePublic',
'riegeneinteilungPublic'
];
$type = $_POST['type'] ? trim($_POST['type']) : '';
if (!in_array($type, $allowedTypes)) {
echo json_encode(['success' => false, 'message' => 'Invalid input']);
exit;
}
$value = $_POST['value'] ? trim($_POST['value']) : null;
// ---------- Step 2: Get values from DB ----------
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_var (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical db error']);
exit;
}
$stmt->bind_param("ss", $type, $value);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
// Return JSON
echo json_encode([
'success' => true
]);
exit;
@@ -0,0 +1,93 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
$action = $_POST['action'] ?? '';
if ($action === 'add') {
$name = trim($_POST['name'] ?? '');
$start_index = intval($_POST['start_index'] ?? 0);
$color = trim($_POST['color'] ?? '#424242');
$audiofile = intval($_POST['audiofile'] ?? 0);
$audiofile_max_duration = (int) $_POST['audiofileMaxDuration'] ?? 0;
if ($name === '') {
echo json_encode(['success' => false, 'message' => 'Name ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_disziplinen (`name`, `start_index`, `color_kampfrichter`, `audiofile`, `audiofile_max_duration`) VALUES (?, ?, ?, ?, ?)");
$stmt->bind_param("sisii", $name, $start_index, $color, $audiofile, $audiofile_max_duration);
$success = $stmt->execute();
$new_id = $mysqli->insert_id;
$stmt->close();
if ($success) {
echo json_encode(['success' => true, 'id' => $new_id]);
} else {
echo json_encode(['success' => false, 'message' => 'Fehler beim Hinzufügen.']);
}
generateIntersectCache($mysqli, $baseDir);
} elseif ($action === 'update') {
$id = intval($_POST['id'] ?? 0);
$field = $_POST['field'] ?? '';
$value = $_POST['value'] ?? '';
$audiofile = intval($_POST['audiofile'] ?? 0);
$allowedFields = ['name', 'start_index', 'color_kampfrichter', 'audiofile', 'audiofile_max_duration'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'start_index' || $field === 'audiofile' || $field === 'audiofile_max_duration') {
$value = intval($value);
}
$updated = db_update($mysqli, $db_tabelle_disziplinen, [$field => $value], ['id' => $id]);
if ($updated !== false) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'DB Update failed.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
}
} elseif ($action === 'delete') {
$id = intval($_POST['id'] ?? 0);
if ($id > 0) {
db_delete($mysqli, $db_tabelle_disziplinen, ['id' => $id]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
}
generateIntersectCache($mysqli, $baseDir);
} else {
echo json_encode(['success' => false, 'message' => 'Action not found.']);
}
@@ -0,0 +1,281 @@
<?php
ini_set("display_errors",1);
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-noten-cache.php';
$recalculateJSONs = false;
$action = $_POST['action'] ?? '';
if ($action === 'add') {
$name = trim($_POST['name'] ?? '');
$type_val = $_POST['type_val'] ?? 'input';
$berechnung = trim($_POST['berechnung'] ?? '');
$pro_geraet = intval($_POST['pro_geraet'] ?? 0);
$default_value = (isset($_POST['default_value'])) ? floatval($_POST['default_value']) : null;
$min_value = (isset($_POST['min_value'])) ? floatval($_POST['min_value']) : null;
$max_value = (isset($_POST['max_value'])) ? floatval($_POST['max_value']) : null;
$id = (isset($_POST['id'])) ? floatval($_POST['id']) : 1;
if (!$id) {
echo json_encode(['success' => false, 'message' => 'Id ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("SELECT 1 FROM $db_tabelle_wertungstypen WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo json_encode([
'success' => false,
'message' => 'Eine Note mit dieser Id existiert bereits.'
]);
$stmt->close();
exit;
}
$stmt->close();
if (!$name) {
echo json_encode(['success' => false, 'message' => 'Name ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_wertungstypen (id, name, type, berechnung, pro_geraet, default_value, min_value, max_value) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("isssisss", $id, $name, $type_val, $berechnung, $pro_geraet, $default_value, $min_value, $max_value);
$success = $stmt->execute();
$new_id = $mysqli->insert_id;
$stmt->close();
$recalculateJSONs = true;
if ($success) {
regenerate_noten_cache($mysqli, $db_tabelle_wertungstypen, $baseDir);
generateIntersectCache($mysqli, $baseDir);
if (!$recalculateJSONs) { echo json_encode(['success' => true, 'id' => $new_id]); }
} else {
echo json_encode(['success' => false, 'message' => 'Fehler beim Hinzufügen.']);
exit;
}
} elseif ($action === 'update') {
$id = intval($_POST['id'] ?? 0);
$field = $_POST['field'] ?? '';
$value = $_POST['value'] ?? '';
$allowedFields = ['name', 'type', 'berechnung', 'pro_geraet', 'geraete_json', 'anzahl_laeufe_json', 'default_value', 'min_value', 'max_value', 'zeige_auf_rangliste', 'nullstellen', 'display_string', 'groesse_auf_rangliste'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'pro_geraet') {
$value = intval($value);
}
if ($field === 'berechnung' || $field === 'type') {
$recalculateJSONs = true;
}
$updated = db_update($mysqli, $db_tabelle_wertungstypen, [$field => $value], ['id' => $id]);
if ($updated !== false) {
regenerate_noten_cache($mysqli, $db_tabelle_wertungstypen, $baseDir);
generateIntersectCache($mysqli, $baseDir);
if (!$recalculateJSONs) { echo json_encode(['success' => true]); }
} else {
echo json_encode(['success' => false, 'message' => 'DB Update failed.']);
exit;
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
exit;
}
} elseif ($action === 'delete') {
$id = intval($_POST['id'] ?? 0);
if ($id > 0) {
db_delete($mysqli, $db_tabelle_wertungstypen, ['id' => $id]);
regenerate_noten_cache($mysqli, $db_tabelle_wertungstypen, $baseDir);
generateIntersectCache($mysqli, $baseDir);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Action not found.']);
exit;
}
if ($recalculateJSONs) {
$noten = db_select($mysqli, $db_tabelle_wertungstypen, "id, berechnung, type");
// 1. Re-index the array so the keys match the database IDs
$notenById = array_column($noten, null, 'id');
$berechnungen = [];
foreach ($notenById as $id => $sn) {
if ($sn['type'] === 'berechnung') {
$berechnungen[] = $sn;
}
}
if (empty($berechnungen)) {
echo json_encode(['success' => true, 'message' => "Keine Berechnungen ausgewählt"]);
exit;
}
$notenRechner = new NotenRechner();
// 1. Build the direct map
// Format: [ Changed_Note_ID => [ "CalcId|GeraetId" => [CalcId, GeraetId] ] ]
$dependencyMap = [];
foreach ($berechnungen as $calc) {
$neededIdsArrayWithRun = $notenRechner->getBenoetigteIdsComplexWithRun($calc['berechnung']);
if (empty($neededIdsArrayWithRun) || count($neededIdsArrayWithRun) < 2) {
continue;
}
$calcId = (int)$calc['id'];
$neededIdsArray = $neededIdsArrayWithRun;
unset($neededIdsArray['targetRun']);
unset($neededIdsArray['targetGeraet']);
$targetRunId = $neededIdsArrayWithRun['targetRun'];
$target_geraet_id = $neededIdsArrayWithRun['targetGeraet'];
foreach ($neededIdsArray as $key => $needed) {
$nId = (int)$needed['noteId'];
// Keep geraetId as integer if it's a number (e.g., 3), otherwise string ('S')
$gId = is_numeric($needed['geraetId']) ? (int)$needed['geraetId'] : $needed['geraetId'];
// Create a unique string key so we don't store exact duplicates
$nodeKey = $calcId . '|' . $gId . '|' . $targetRunId . '|' . $target_geraet_id; // e.g., "10|S|R2" or "12|3|RA"
if (!isset($dependencyMap[$nId])) {
$dependencyMap[$nId] = [];
}
// Store it as the "little array" you requested: [DependentCalcId, GeraetId]
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId, [$targetRunId, $target_geraet_id]];
}
}
// 2. Our recursive helper function (Updated for complex nodes)
function getCompleteDependencyChain($id, $directMap, $visited = [])
{
// If this ID doesn't have anything depending on it, return empty
if (!isset($directMap[$id])) {
return [];
}
$allDependencies = [];
foreach ($directMap[$id] as $nodeKey => $complexNode) {
// CIRCULAR DEPENDENCY CHECK:
// We check against the string key (e.g., "10|S") to prevent infinite loops
if (isset($visited[$nodeKey])) {
continue;
}
// 1. Mark this specific node as visited
$visited[$nodeKey] = true;
// 2. Add the little array [CalcId, GeraetId] to our master list
$allDependencies[$nodeKey] = $complexNode;
// 3. Recursively find everything that depends on THIS calculation ID
// $complexNode[0] is the dependent Calc ID
$childDependencies = getCompleteDependencyChain($complexNode[0], $directMap, $visited);
// 4. Merge the child results into our master list safely
foreach ($childDependencies as $childKey => $childNode) {
$allDependencies[$childKey] = $childNode;
$visited[$childKey] = true; // Ensure the parent loop knows this was visited
}
}
return $allDependencies;
}
// 3. Create the final flattened map for ALL IDs
$flatDependencyMap = [];
foreach (array_keys($notenById) as $id) {
$chain = getCompleteDependencyChain($id, $dependencyMap);
// Only add it if dependencies exist
if (!empty($chain)) {
// array_values() removes the "10|S" string keys, turning it into a perfect
// 0-indexed array for clean JSON encoding: [[10, "S"], [12, 3]]
$flatDependencyMap[$id] = array_values($chain);
}
}
// 4. Database Updates
// Step 1: Reset all rows to NULL in a single query
$resetSql = "UPDATE $db_tabelle_wertungstypen SET `berechnung_json` = NULL";
$mysqli->query($resetSql);
// Step 2: Prepare the statement
$updateSql = "UPDATE $db_tabelle_wertungstypen SET `berechnung_json` = ? WHERE id = ?";
$stmt = $mysqli->prepare($updateSql);
foreach ($flatDependencyMap as $id => $completeDependencyArray) {
if (empty($completeDependencyArray)) {
continue;
}
$jsonString = json_encode($completeDependencyArray);
// Bind parameters: 's' for string (JSON), 'i' for integer (ID)
$stmt->bind_param("si", $jsonString, $id);
$stmt->execute();
}
$stmt->close();
echo json_encode(['success' => true, 'message' => "Abhaengigkeiten berechnet"]);
exit;
}
@@ -0,0 +1,41 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($_POST['css_content'])) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Missing css_content parameter.']);
exit;
}
$css_content = $_POST['css_content'] ?? '';
if (strlen($css_content) > 200000) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'File size too large.']);
exit;
}
if (preg_match('/javascript:/i', $css_content) || preg_match('/<script/i', $css_content)) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid characters detected.']);
exit;
}
file_put_contents($baseDir . '/intern/css/user.css', $css_content);
http_response_code(200);
echo json_encode(['success' => true, 'message' => 'CSS saved successfully.']);
exit;
@@ -0,0 +1,205 @@
<?php
/**
* Upload Image API
* Accepts multipart/form-data POST with 'image' file.
* Converts JPG/PNG to WebP (resized to max 2400px), saves to /files/img/admin_upload/{uid}.webp
* Returns JSON: { success: true }
*/
// Allow large uploads and enough memory for GD processing
ini_set('memory_limit', '256M');
ini_set('upload_max_filesize', '50M');
ini_set('post_max_size', '55M');
ini_set('max_execution_time', '120');
const MAX_DIMENSION = 2400;
const MAX_DIMENSION_ICON = 1000;
const WEBP_QUALITY = 80;
$baseDir = $_SERVER['DOCUMENT_ROOT'];
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
// Only accept POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
exit;
}
$allowedTypes = ['Kampfrichter', 'Trainer', 'Wk_leitung', 'Otl', 'icon', 'logo-normal'];
if (!isset($_POST['type']) || !in_array($_POST['type'], $allowedTypes)) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Typ nicht angegeben oder nicht erlaubt']);
exit;
}
$pngs = ['icon', 'logo-normal'];
$type = $_POST['type'];
$isIcon = in_array($type, $pngs);
// Check if $_FILES is empty entirely (can happen if post_max_size exceeded)
if (empty($_FILES)) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'No files received. The file may exceed the server upload limit).',
]);
exit;
}
// Check specific file field
if (!isset($_FILES['image'])) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'No "image" field in upload. Received fields: ' . implode(', ', array_keys($_FILES)),
]);
exit;
}
if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
$errors = [
UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize',
UPLOAD_ERR_FORM_SIZE => 'File exceeds form MAX_FILE_SIZE',
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
UPLOAD_ERR_NO_TMP_DIR => 'Missing temp directory',
UPLOAD_ERR_CANT_WRITE => 'Failed to write to disk',
];
$code = $_FILES['image']['error'];
$msg = $errors[$code] ?? "Unknown error code: $code";
echo json_encode(['success' => false, 'message' => $msg]);
exit;
}
$file = $_FILES['image'];
$tmpPath = $file['tmp_name'];
// Validate MIME type
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($tmpPath);
$allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
if (!in_array($mime, $allowedMimes, true)) {
http_response_code(415);
echo json_encode(['success' => false, 'message' => "Unsupported file type: $mime"]);
exit;
}
// Target directory
if ($isIcon) {
$uploadDir = $baseDir . '/intern/img/';
} else {
$uploadDir = $baseDir . '/intern/img/login';
}
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Load image into GD
$srcImage = null;
switch ($mime) {
case 'image/jpeg':
$srcImage = @imagecreatefromjpeg($tmpPath);
break;
case 'image/png':
$srcImage = @imagecreatefrompng($tmpPath);
break;
case 'image/webp':
$srcImage = @imagecreatefromwebp($tmpPath);
break;
case 'image/gif':
$srcImage = @imagecreatefromgif($tmpPath);
break;
}
if (!$srcImage) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Failed to load image into GD. It may be corrupt or too large.']);
exit;
}
// ── Resize if needed ────────────────────────────────
$origW = imagesx($srcImage);
$origH = imagesy($srcImage);
$maxDimension = ($isIcon) ? MAX_DIMENSION_ICON : MAX_DIMENSION;
if ($origW > $maxDimension || $origH > $maxDimension) {
// Calculate new dimensions keeping aspect ratio
if ($origW >= $origH) {
$newW = $maxDimension;
$newH = intval(round($origH * ($maxDimension / $origW)));
} else {
$newH = $maxDimension;
$newW = intval(round($origW * ($maxDimension / $origH)));
}
$resized = imagecreatetruecolor($newW, $newH);
// Preserve transparency
imagealphablending($resized, false);
imagesavealpha($resized, true);
$transparent = imagecolorallocatealpha($resized, 0, 0, 0, 127);
imagefill($resized, 0, 0, $transparent);
imagecopyresampled($resized, $srcImage, 0, 0, 0, 0, $newW, $newH, $origW, $origH);
imagedestroy($srcImage);
$srcImage = $resized;
} else {
// Still preserve transparency
imagepalettetotruecolor($srcImage);
imagealphablending($srcImage, true);
imagesavealpha($srcImage, true);
}
if ($isIcon) {
// ── Save as PNG ────────────────────────────────────
$filename = $type . '.png';
$destPath = $uploadDir . '/' . $filename;
$webPath = '/intern/img/' . $filename;
$pngCompression = 6;
$success = imagepng($srcImage, $destPath, $pngCompression);
} else {
// ── Save as WebP ────────────────────────────────────
$filename = 'bg' . $_POST['type'] . '.webp';
$destPath = $uploadDir . '/' . $filename;
$webPath = '/intern/img/admin_upload/' . $filename;
$success = imagewebp($srcImage, $destPath, WEBP_QUALITY);
}
imagedestroy($srcImage);
if (!$success) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Failed to convert to WebP']);
exit;
}
echo json_encode([
'success' => true
]);
@@ -0,0 +1,87 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$action = $_POST['action'] ?? '';
if ($action === 'add') {
$name = trim($_POST['name'] ?? '');
$has_endtime = intval($_POST['has_endtime'] ?? 0);
$index = intval($_POST['index'] ?? 0);
if (!$name) {
echo json_encode(['success' => false, 'message' => 'Name ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_zeitplan_types (`name`, `has_endtime`, `display_index`) VALUES (?, ?, ?)");
$stmt->bind_param("sii", $name, $has_endtime, $index);
$success = $stmt->execute();
$new_id = $mysqli->insert_id;
$stmt->close();
if ($success) {
echo json_encode(['success' => true, 'id' => $new_id]);
} else {
echo json_encode(['success' => false, 'message' => 'Fehler beim Hinzufügen.']);
}
} elseif ($action === 'update') {
$id = intval($_POST['id'] ?? 0);
$field = $_POST['field'] ?? '';
$value = $_POST['value'] ?? '';
$audiofile = intval($_POST['audiofile'] ?? 0);
$allowedFields = ['name', 'has_endtime', 'display_index'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'display_index') {
$value = intval($value);
}
$updated = db_update($mysqli, $db_tabelle_zeitplan_types, [$field => $value], ['id' => $id]);
if ($updated !== false) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'DB Update failed.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
}
} elseif ($action === 'delete') {
$id = intval($_POST['id'] ?? 0);
if ($id > 0) {
db_delete($mysqli, $db_tabelle_zeitplan_types, ['id' => $id]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Action not found.']);
}
@@ -0,0 +1,111 @@
<?php
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
try {
$mysqli->set_charset("utf8mb4");
// 4. Fetch all rows ordered by ID so the layout hierarchy is preserved
$sql = "SELECT * FROM `$db_tabelle_wertungstypen` ORDER BY `id` ASC";
$result = $mysqli->query($sql);
$exportData = [];
while ($row = $result->fetch_assoc()) {
$presetItem = [];
foreach ($row as $columnName => $value) {
if ($columnName === 'berechnung_json') {
continue;
}
if ($value === null) {
$presetItem[$columnName] = null;
} elseif (is_numeric($value)) {
$presetItem[$columnName] = (strpos($value, '.') !== false) ? (float)$value : (int)$value;
} else {
$presetItem[$columnName] = $value;
}
}
$exportDataDisc[] = $presetItem;
}
$result->free();
$geraete = db_select($mysqli, $db_tabelle_disziplinen, '*', '', [], 'start_index ASC');
$programme = db_select($mysqli, $db_tabelle_kategorien, '*', '', [], 'id ASC');
$zeitplanTypen = db_select($mysqli, $db_tabelle_zeitplan_types, '*', '', [], 'id ASC');
$rangNote = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['rangNote']);
$wkName = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);
$orderBestRang = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['orderBestRang']);
$personEinzahl = db_get_variable($mysqli, $db_tabelle_var, ['personEinzahl']);
if ($personEinzahl === null) $personEinzahl = 'Person';
$personMehrzahl = db_get_variable($mysqli, $db_tabelle_var, ['personMehrzahl']);
if ($personMehrzahl === null) $personMehrzahl = 'Personen';
$mysqli->close();
$exportData = [
'metadata' => [
'erstellt_am' => date("d.m.Y H:i"),
'ersteller' => $_SERVER['HTTP_HOST'],
'erstellt_mit' => 'WKVS Auto-Export',
'wkvs_json_version' => '1.0.0',
'titel' => 'WKVS Preset ' . $wkName . ' (V 1.0.0)'
],
'noten' => $exportDataDisc,
'disziplinen' => $geraete,
'zeitplan_typen' => $zeitplanTypen,
'rang_konfiguration' => [
'rang_note_id' => $rangNote,
'order_best_rang' => $orderBestRang
],
"leistungsklassen" => $programme,
"bezeichnungen" =>[
"singular" => $personEinzahl,
"plural"=> $personMehrzahl
]
];
$jsonOutput = json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// 6. Force Browser File Download
$fileName = 'WKVS-preset-config-' . date('Y-m-d_H-i') . '.json';
header('Content-Type: application/json; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . strlen($jsonOutput));
echo $jsonOutput;
exit;
} catch (mysqli_sql_exception $e) {
http_response_code(500);
echo "Database export failed";
}
@@ -0,0 +1,350 @@
<?php
ini_set("display_errors",1);
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($_FILES['configFile']) || $_FILES['configFile']['error'] !== UPLOAD_ERR_OK) {
echo json_encode([
'success' => false,
'message' => 'Keine Config-Datei ausgewählt'
]);
exit;
}
$wkvsJSONVersion = '1.0.0';
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$saveDir = '/../private-files/config-uploads/';
$uploadDir = $baseDir . $saveDir;
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$tmpPath = $_FILES['configFile']['tmp_name'];
$originalName = $_FILES['configFile']['name'];
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$allowedExt = ['json'];
if (!in_array($extension, $allowedExt, true)) {
echo json_encode(['success' => false, 'message' => 'Falsches Format (Endung)']);
http_response_code(422);
exit;
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($tmpPath);
$allowedMime = ['application/json'];
if (!in_array($mimeType, $allowedMime, true)) {
echo json_encode(['success' => false, 'message' => 'Dateiinhalt ist kein gültiges JSON']);
http_response_code(422);
exit;
}
$filename = date("Y_m_d_H_i_s") . '-wkvs-config.json';
$destination = $uploadDir . $filename;
if (!move_uploaded_file($tmpPath, $destination)) {
http_response_code(500);
exit;
}
$JSONcontent = file_get_contents($destination);
$content = json_decode($JSONcontent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
die("Invalid JSON layout.");
}
if (!isset($content['metadata']['wkvs_json_version']) || $content['metadata']['wkvs_json_version'] !== $wkvsJSONVersion) {
echo json_encode(['success' => false, 'message' => 'Inkompatible Datei-Version. (V ' . $wkvsJSONVersion . ' benötigt'. (isset($content['metadata']['wkvs_json_version']) ? ", benutzte Datei-Version: V " . $content['metadata']['wkvs_json_version'] : '') . ')' ]);
unlink($destination);
http_response_code(422);
exit;
}
if (!isset($content['noten']) || !is_array($content['noten']) || !isset($content['disziplinen']) || !is_array($content['disziplinen'])) {
echo json_encode(['success' => false, 'message' => 'Ungültige JSON-Struktur']);
unlink($destination);
http_response_code(422);
exit;
}
$content['added_timestamp'] = date("Y-m-d H:i:s");
file_put_contents($destination, json_encode($content));
$allowedColumns = [
'noten' => [
'id', 'name', 'default_value', 'type', 'berechnung', 'berechnung_json',
'max_value', 'min_value', 'pro_geraet', 'anzahl_laeufe_json', 'geraete_json',
'nullstellen', 'display_string'
],
'disziplinen' => ['id', 'name', 'start_index', 'color_kampfrichter', 'audiofile'],
'zeitplan_typen' => ['id', 'name', 'has_endtime', 'display_index'],
'leistungsklassen' => ['id', 'programm', 'order_index', 'preis', 'aktiv']
];
$tableTable = [
'noten' => $db_tabelle_wertungstypen,
'disziplinen' => $db_tabelle_disziplinen,
'zeitplan_typen' => $db_tabelle_zeitplan_types,
'leistungsklassen' => $db_tabelle_kategorien
];
$successCount = 0;
try {
$mysqli->begin_transaction();
foreach ($allowedColumns as $type => $columns) {
$tableName = "`" . $tableTable[$type] . "`";
$mysqli->query("DELETE FROM " . $tableName);
$columnsSql = implode(', ', array_map(fn($col) => "`$col`", $columns));
$placeholdersSql = implode(', ', array_fill(0, count($columns), '?'));
$sql = "INSERT INTO " . $tableName . " ($columnsSql)
VALUES ($placeholdersSql)";
$typesString = str_repeat('s', count($columns));
$stmt = $mysqli->prepare($sql);
foreach ($content[$type] as $index => $row) {
$bindValues = [];
foreach ($columns as $column) {
if (!array_key_exists($column, $row) || $row[$column] === null) {
$bindValues[] = null;
} else {
$bindValues[] = $row[$column];
}
}
$stmt->bind_param($typesString, ...$bindValues);
$stmt->execute();
$successCount++;
}
$stmt->close();
}
$rangNote = intval($content['rang_konfiguration']['rang_note_id'] ?? 0);
$orderBestRang = $content['rang_konfiguration']['order_best_rang'] ?? '';
$allowableOrderValues = ['ASC', 'DESC'];
if ($rangNote > 0) {
$mysqli->query("INSERT INTO $db_tabelle_var (`name`, `value`) VALUES ('rangNote', '" . $rangNote . "') ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
}
if (in_array($orderBestRang, $allowableOrderValues, true)) {
$mysqli->query("INSERT INTO $db_tabelle_var (`name`, `value`) VALUES ('orderBestRang', '" . $orderBestRang . "') ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
}
if (isset($content['bezeichnungen']['singular']) && $content['bezeichnungen']['singular'] !== null) {
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_var (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
$varName = 'personEinzahl';
$stmt->bind_param("ss", $varName, $content['bezeichnungen']['singular']);
$stmt->execute();
$stmt->close();
}
if (isset($content['bezeichnungen']['plural']) && $content['bezeichnungen']['plural'] !== null) {
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_var (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
$varName = 'personMehrzahl';
$stmt->bind_param("ss", $varName, $content['bezeichnungen']['plural']);
$stmt->execute();
$stmt->close();
}
$noten = db_select($mysqli, $db_tabelle_wertungstypen, "id, berechnung, type");
$notenById = array_column($noten, null, 'id');
$berechnungen = [];
foreach ($notenById as $id => $sn) {
if ($sn['type'] === 'berechnung') {
$berechnungen[] = $sn;
}
}
if (empty($berechnungen)) {
$mysqli->commit();
echo json_encode(['success' => true, 'message' => "Successfully imported $successCount rows."]);
exit;
}
$notenRechner = new NotenRechner();
$dependencyMap = [];
foreach ($berechnungen as $calc) {
$neededIdsArrayWithRun = $notenRechner->getBenoetigteIdsComplexWithRun($calc['berechnung']);
if (empty($neededIdsArrayWithRun) || count($neededIdsArrayWithRun) < 2) {
continue;
}
$calcId = (int)$calc['id'];
$neededIdsArray = $neededIdsArrayWithRun;
unset($neededIdsArray['targetRun']);
unset($neededIdsArray['targetGeraet']);
$targetRunId = $neededIdsArrayWithRun['targetRun'];
foreach ($neededIdsArray as $key => $needed) {
$nId = (int)$needed['noteId'];
$gId = is_numeric($needed['geraetId']) ? (int)$needed['geraetId'] : $needed['geraetId'];
$nodeKey = $calcId . '|' . $gId . '|' . $targetRunId;
if (!isset($dependencyMap[$nId])) {
$dependencyMap[$nId] = [];
}
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId, [$targetRunId]];
}
}
function getCompleteDependencyChain($id, $directMap, $visited = [])
{
if (!isset($directMap[$id])) {
return [];
}
$allDependencies = [];
foreach ($directMap[$id] as $nodeKey => $complexNode) {
if (isset($visited[$nodeKey])) {
continue;
}
$visited[$nodeKey] = true;
$allDependencies[$nodeKey] = $complexNode;
$childDependencies = getCompleteDependencyChain($complexNode[0], $directMap, $visited);
foreach ($childDependencies as $childKey => $childNode) {
$allDependencies[$childKey] = $childNode;
$visited[$childKey] = true;
}
}
return $allDependencies;
}
$flatDependencyMap = [];
foreach (array_keys($notenById) as $id) {
$chain = getCompleteDependencyChain($id, $dependencyMap);
if (!empty($chain)) {
$flatDependencyMap[$id] = array_values($chain);
}
}
$resetSql = "UPDATE $db_tabelle_wertungstypen SET `berechnung_json` = NULL";
$mysqli->query($resetSql);
// Step 2: Prepare the statement
$updateSql = "UPDATE $db_tabelle_wertungstypen SET `berechnung_json` = ? WHERE id = ?";
$stmt = $mysqli->prepare($updateSql);
foreach ($flatDependencyMap as $id => $completeDependencyArray) {
if (empty($completeDependencyArray)) {
continue;
}
$jsonString = json_encode($completeDependencyArray);
$stmt->bind_param("si", $jsonString, $id);
$stmt->execute();
}
$stmt->close();
$mysqli->commit();
echo json_encode(['success' => true, 'message' => "Successfully imported $successCount rows."]);
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-noten-cache.php';
generateIntersectCache($mysqli, $baseDir);
regenerate_noten_cache($mysqli, $db_tabelle_wertungstypen, $baseDir);
} catch (mysqli_sql_exception $e) {
$mysqli->rollback();
unlink($destination);
if ($e->getCode() === 1048) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => "Import failed: A required data field is missing in your preset file."]);
exit;
} else {
http_response_code(400);
echo json_encode(['success' => false, 'message' => "Database error during import." . " Error Code: " . $e->getMessage()]);
exit;
}
} finally {
$mysqli->close();
$allFiles = glob($uploadDir . '*.json');
$timestamps = array_map(function($file) {
$jsonContent = file_get_contents($file) ?? '';
$data = json_decode($jsonContent, true) ?? [];
return $data['added_timestamp'] ?? 0;
}, $allFiles);
array_multisort($timestamps, SORT_DESC, $allFiles);
$filesToDelete = array_slice($allFiles, 3);
foreach ($filesToDelete as $file) {
unlink($file);
}
}
@@ -0,0 +1,85 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
ini_set('display_errors', 1);
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
check_user_permission('kampfrichter');
require_once $baseDir . '/../scripts/delete_request_type_functions.php';
parse_input_to_delete();
verify_delete_csrf($_DELETE);
$programm_id = (int) ($_DELETE['programmId'] ?? 0);
if ($programm_id <= 0) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Keine valide Programm-ID']);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
$programm_name = db_get_var($mysqli, "SELECT `programm` FROM $db_tabelle_kategorien WHERE id = ? LIMIT 1", [$programm_id]) ?: null;
if ($programm_name === null) {
echo json_encode(['success' => true, 'message' => 'Dieses Programm existiert nicht.']);
http_response_code(400);
exit;
}
$personen = db_select($mysqli, $db_tabelle_teilnehmende, "id", '`programm` = ?', [$programm_name]);
if (count($personen) < 1) {
echo json_encode(['success' => true, 'message' => 'Es existieren keine Personen mit diesem Programm.']);
http_response_code(400);
exit;
}
$personen_id_array = array_column($personen, 'id');
$parram_array = array_fill(0, count($personen_id_array), "?");
$parram_string = implode(', ', $parram_array);
$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;
$value_array = array_merge($personen_id_array, [$current_wk_id]);
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_wertungen WHERE `person_id` IN ($parram_string) AND `wk_id` = ?");
$types_string = str_repeat("i", count($personen_id_array)) . "i";
$stmt->bind_param($types_string, ...$value_array);
$stmt->execute();
$stmt->close();
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_wertungen_log WHERE `person_id` IN ($parram_string) AND `wk_id` = ?");
$stmt->bind_param($types_string, ...$value_array);
$stmt->execute();
$stmt->close();
echo json_encode(['success' => true, 'message' => 'Noten gelöscht']);
http_response_code(200);
@@ -0,0 +1,87 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$abt_id = intval($_POST['abteilungId'] ?? 1);
// Determine current year
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT
t.id AS person_id
FROM $db_tabelle_teilnehmende_gruppen tab
INNER JOIN $db_tabelle_teilnehmende t ON tab.turnerin_id = t.id
INNER JOIN $db_tabelle_gruppen ab ON ab.id = tab.abteilung_id
WHERE ab.id = ?
ORDER BY t.id ASC
");
$stmt->bind_param('i', $abt_id);
$stmt->execute();
$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);
$indexedPersonen = array_column($personen, null, 'person_id');
// Close statement
$stmt->close();
// 1. Get the IDs from the first query results
$personenIds = array_column($personen, 'person_id');
if (empty($personenIds)) {
echo json_encode(["success" => false, "message" => "Keine Notenänderungen vorhanden"]);
http_response_code(400);
exit;
}
$placeholders = implode(',', array_fill(0, count($personenIds), '?'));
$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;
$sqlNotenChanges = "DELETE FROM $db_tabelle_wertungen_log WHERE person_id IN ($placeholders) AND `wk_id` = ?";
$stmtNotenChanges = $mysqli->prepare($sqlNotenChanges);
$paramsArray = array_merge($personenIds, [$current_wk_id]);
$types = str_repeat('i', count($personenIds)) . 's';
$stmtNotenChanges->bind_param($types, ...$paramsArray);
$stmtNotenChanges->execute();
$stmtNotenChanges->close();
echo json_encode(["success" => true, "message" => "Notenänderung für Abteilung " . $abt_id . " gelöscht"]);
http_response_code(200);
@@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$wkName = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);
$mysqli->close();
if ($wkName === null) {
echo json_encode(["success" => false, "message" => "Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen"]);
http_response_code(400);
exit;
}
$programm = trim($_POST['prog'] ?? '');
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;
$dir = $baseDir . '/files/ranglisten/';
$localPath = $dir . str_replace("/", "", str_replace(" ", "_", $wkName)) . "_Ergebnisse_" . str_replace("/", "", $programm) . "_" . $current_year . ".pdf";
if (!file_exists($localPath)) {
echo json_encode(["success" => false, "message" => "Diese Rangliste existiert nicht."]);
http_response_code(400);
exit;
}
unlink($localPath);
echo json_encode(["success" => true, "message" => "Rangliste gelöscht"]);
http_response_code(200);
@@ -0,0 +1,341 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
// Validate editId from POST
if (isset($_POST['editId'])) {
$editId = intval($_POST['editId']);
if ($editId === false || $editId < 1) {
echo json_encode(['success' => false, 'message' => 'Falsche Personen ID']);
exit;
}
}
$editId = filter_var($editId, FILTER_VALIDATE_INT);
if ($editId === false) {
echo json_encode(['success' => true]);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if (!($data['success'] ?? false)) {
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
$is_payed = db_get_var($mysqli, "SELECT 1 FROM $db_tabelle_teilnehmende WHERE id = ? AND ((bezahltoverride = ?) OR (bezahlt = ? AND bezahltoverride = ?))", [$editId, 4, 4, 0]);
if ($is_payed != '1') {
echo json_encode(['success'=> false, 'message'=> 'Startgebühr nicht bezahlt']);
http_response_code(400);
exit;
}
$isAdmin = (($_SESSION['selectedFreigabeIdKampfrichter'] ?? '') === 'A') ? true : false;
$disciplines = db_select($mysqli, $db_tabelle_disziplinen, 'id', '', [], 'start_index ASC');
$disciplines = array_column($disciplines, "id");
$requested_discipline = trim($_POST['geraet'] ?? '');
$all_disciplines = false;
if ($isAdmin && $requested_discipline === 'A') {
$all_disciplines = true;
} else {
$requested_discipline = (int) $requested_discipline;
if (!in_array($requested_discipline, $disciplines)) {
echo json_encode(['success' => false, 'message' => 'Falsche Geräte ID']);
exit;
}
$discipline = $requested_discipline;
}
if ($all_disciplines) {
$stmt = $mysqli->prepare("SELECT t.`name`, t.`vorname`, t.`programm`, p.id as programm_id FROM $db_tabelle_teilnehmende t LEFT JOIN $db_tabelle_kategorien p ON p.programm = t.programm WHERE t.id = ?");
} else {
$disciplines = [$discipline];
$stmt = $mysqli->prepare("
SELECT
t.name,
t.vorname,
t.programm,
p.id as programm_id,
agg.abteilung,
agg.geraeteIndex,
agg.startIndex
FROM $db_tabelle_teilnehmende t
LEFT JOIN $db_tabelle_kategorien p ON p.programm = t.programm
LEFT JOIN (
SELECT
ta.turnerin_id,
GROUP_CONCAT(DISTINCT a.name SEPARATOR ', ') AS abteilung,
GROUP_CONCAT(DISTINCT g.start_index SEPARATOR ', ') AS geraeteIndex,
ta.turnerin_index AS startIndex
FROM $db_tabelle_teilnehmende_gruppen ta
INNER JOIN $db_tabelle_gruppen a
ON a.id = ta.abteilung_id
LEFT JOIN $db_tabelle_disziplinen g
ON g.id = ta.geraet_id
GROUP BY ta.turnerin_id
) agg ON agg.turnerin_id = t.id
WHERE t.id = ?
");
}
$stmt->bind_param('i', $editId);
$stmt->execute();
$result = $stmt->get_result();
$dbresult = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
if (!$dbresult || !is_array($dbresult) || count($dbresult) < 1) {
echo json_encode(['success' => false, 'message' => 'Falsche Personen ID']);
exit;
}
$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;
if ($all_disciplines) {
$stmt = $mysqli->prepare("SELECT `note_bezeichnung_id`, `value`, `geraet_id`, `run_number` FROM $db_tabelle_wertungen WHERE `person_id` = ? AND `wk_id` = ?");
$stmt->bind_param('si', $editId, $current_wk_id);
} else {
$stmt = $mysqli->prepare("SELECT `note_bezeichnung_id`, `value`, `geraet_id`, `run_number` FROM $db_tabelle_wertungen WHERE `person_id` = ? AND `geraet_id` = ? AND `wk_id` = ?");
$stmt->bind_param('ssi', $editId, $discipline, $current_wk_id);
}
$stmt->execute();
$result = $stmt->get_result();
$notenDB = $result->fetch_all(MYSQLI_ASSOC);
$indexedNotenDB = [];
foreach ($notenDB as $sn) {
$indexedNotenDB[$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = $sn['value'];
}
$stmt->close();
$stmt = $mysqli->prepare("SELECT `id`, `default_value`, `nullstellen`, `pro_geraet`, `geraete_json`, `anzahl_laeufe_json` FROM $db_tabelle_wertungstypen");
$stmt->execute();
$result = $stmt->get_result();
$notenConfig = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$displayIdNoteL = intval(db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['displayIdNoteL'])) ?? 0;
$displayIdNoteR = intval(db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['displayIdNoteR'])) ?? 0;
if ($displayIdNoteL !== 0 && $displayIdNoteR !== 0) {
$displayNoten = [$displayIdNoteR => 0, $displayIdNoteL => 0];
}
$noten = [];
$row = $dbresult[0];
$programm_id = $row['programm_id'];
foreach ($disciplines as $d) {
foreach ($notenConfig as $snC) {
$allowedGeraete = !empty($snC['geraete_json']) ? json_decode($snC['geraete_json'], true) : [];
$isProGeraet = ($snC['pro_geraet'] === 1);
if (!$isProGeraet && !in_array($d, $allowedGeraete)) {
continue;
}
// Determine number of runs for this program
$anzRunsConfig = !empty($snC['anzahl_laeufe_json']) ? json_decode($snC['anzahl_laeufe_json'], true) : [];
$runs = $anzRunsConfig[$d][$programm_id] ?? $anzRunsConfig[$d]["all"] ?? $anzRunsConfig['default'] ?? 1;
if (isset($displayNoten) && array_key_exists($snC['id'], $displayNoten)) {
$displayNoten[$snC['id']] = $runs;
}
for ($r = 1; $r <= $runs; $r++) {
$value = $indexedNotenDB[$d][$snC['id']][$r] ?? $snC['default_value'] ?? 0;
$noten[$d][$r][$snC['id']] = number_format($value, $snC['nullstellen'] ?? 2);
}
}
}
$countBtn = 1;
if (isset($displayNoten)) {
$countBtn = min($displayNoten);
}
$titel = $row['vorname'].' '.$row['name'].', '.$row['programm'];
if (!$all_disciplines) {
$stmt = $mysqli->prepare("
SELECT
t.name,
t.vorname,
t.programm,
t.id,
agg.abteilung,
agg.geraeteIndex,
agg.startIndex
FROM $db_tabelle_teilnehmende t
LEFT JOIN (
SELECT
ta.turnerin_id,
GROUP_CONCAT(DISTINCT a.name SEPARATOR ', ') AS abteilung,
GROUP_CONCAT(DISTINCT g.start_index SEPARATOR ', ') AS geraeteIndex,
ta.turnerin_index AS startIndex
FROM $db_tabelle_teilnehmende_gruppen ta
INNER JOIN $db_tabelle_gruppen a
ON a.id = ta.abteilung_id
LEFT JOIN $db_tabelle_disziplinen g
ON g.id = ta.geraet_id
GROUP BY ta.turnerin_id
) agg ON agg.turnerin_id = t.id
WHERE agg.abteilung = ? AND agg.geraeteIndex = ?
ORDER BY t.id DESC
");
$bezahlt = 4;
$bezahltoverride = 4;
$stmt->bind_param('ss', $row['abteilung'], $row['geraeteIndex']);
$stmt->execute();
$result = $stmt->get_result();
$entries = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
if (!$entries || !is_array($entries) || count($entries) < 1) {
echo json_encode(['success' => false, 'message' => 'No DB Result for next Turnerin']);
exit;
}
$maxstartindex = count($entries);
if ($maxstartindex < 1) {
$maxstartindex = 1;
}
$csti = (int)$row['startIndex'];
$nsti = $csti + 1;
if ($nsti > $maxstartindex){
$nsti -= $maxstartindex;
}
$rohstartindex = intval($row['startIndex']);
$varstartgeraet = intval($row['geraeteIndex']);
$aktsubabt = $_SESSION['currentsubabt'];
foreach ($disciplines as $index => $sdiscipline) {
if (isset($sdiscipline) && $sdiscipline === $discipline) {
$indexuser = $index;
break;
}
}
$calculedstartindex = $rohstartindex - $indexuser;
$calculedstartindex = $calculedstartindex >= 1 ? $calculedstartindex : $calculedstartindex + $maxstartindex;
$nrow = null;
if ($calculedstartindex !== count($entries)){
$nrow = null;
foreach ($entries as $entry) {
if ($entry['startIndex'] == $nsti) {
$nrow = $entry;
break;
}
}
}
if ($nrow) {
$nturnerin = [
'name' => $nrow['vorname'].' '.$nrow['name'].', '.$nrow['programm'],
'id' => $nrow['id']
];
} else {
$nturnerin = [
'name' => '--- nächste Gruppe ---',
'id' => 0
];
}
}
if ($isAdmin) {
echo json_encode([
'success' => true,
'id' => $editId,
'programm_id' => $programm_id,
'titel' => $titel,
'noten' => $noten,
'countBtn' => $countBtn
]);
} else {
echo json_encode([
'success' => true,
'id' => $editId,
'programm_id' => $programm_id,
'titel' => $titel,
'noten' => $noten,
'nturnerin' => $nturnerin,
'countBtn' => $countBtn
]);
}
@@ -0,0 +1,489 @@
<?php
use TCPDF;
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$wkName = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);
$wkName = ($wkName === null) ? 'Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen' : $wkName;
$normalFontSize = 11;
$marginSide = 10;
$marginTop = 10;
$minMarginBottomTable = 20;
$format = 'A4';
$orientation = 'L';
$abt = intval($_POST['abteilungId'] ?? 1);
$now = trim($_POST['date'] ?? date('Y-m-d H:i:s'));
$geraete = db_select($mysqli, $db_tabelle_disziplinen, "*", '', [], "start_index ASC");
$IndexedGeraete = array_column($geraete, 'name', 'id');
$notenBezeichnungen = db_select($mysqli, $db_tabelle_wertungstypen, "`name`, `id`, `nullstellen`, `anzahl_laeufe_json`", '', [], "");
$IndexedNotenBezeichnungen = array_column($notenBezeichnungen, 'name', 'id');
$IndexedNotenNullstellen = array_column($notenBezeichnungen, 'nullstellen', 'id');
$IndexedNotenJsonGeraete = array_column($notenBezeichnungen, 'anzahl_laeufe_json', 'id');
$programme = db_select($mysqli, $db_tabelle_kategorien, "`programm`, `id`", '', [], "");
$indexedProgramme = array_column($programme, 'id', 'programm');
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT
t.id AS person_id,
t.name,
t.vorname,
t.programm,
t.verein,
ab.id AS abteilung_id,
tab.geraet_id AS start_geraet_id,
tab.turnerin_index
FROM $db_tabelle_teilnehmende_gruppen tab
INNER JOIN $db_tabelle_teilnehmende t ON tab.turnerin_id = t.id
INNER JOIN $db_tabelle_gruppen ab ON ab.id = tab.abteilung_id
WHERE ab.id = ?
ORDER BY t.id ASC
");
$stmt->bind_param('i', $abt);
$stmt->execute();
$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);
$indexedPersonen = array_column($personen, null, 'person_id');
// Close statement
$stmt->close();
// 1. Get the IDs from the first query results
$personenIds = array_column($personen, 'person_id');
$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;
if (!empty($personenIds)) {
$placeholders = implode(',', array_fill(0, count($personenIds), '?'));
$sqlNotenChanges = "SELECT * FROM $db_tabelle_wertungen_log WHERE person_id IN ($placeholders) AND `wk_id` = ?";
$stmtNotenChanges = $mysqli->prepare($sqlNotenChanges);
$paramsArray = array_merge($personenIds, [$current_wk_id]);
$types = str_repeat('i', count($personenIds)) . 's';
$stmtNotenChanges->bind_param($types, ...$paramsArray);
$stmtNotenChanges->execute();
$notenChangesResult = $stmtNotenChanges->get_result();
$notenChangesEntries = $notenChangesResult->fetch_all(MYSQLI_ASSOC);
$stmtNotenChanges->close();
} else {
$notenChangesEntries = [];
}
$kampfrichterIds = array_column($notenChangesEntries, 'edited_by');
if (!empty($kampfrichterIds)) {
$placeholders = implode(',', array_fill(0, count($kampfrichterIds), '?'));
$sqlKampfrichter = "SELECT `id` AS `id_kampfrichter`, `name_person` AS `name_kampfrichter` FROM $db_tabelle_intern_benutzende WHERE id IN ($placeholders)";
$stmtKampfrichter = $mysqli->prepare($sqlKampfrichter);
$types = str_repeat('i', count($kampfrichterIds));
$stmtKampfrichter->bind_param($types, ...$kampfrichterIds);
$stmtKampfrichter->execute();
$kampfrichterResult = $stmtKampfrichter->get_result();
$kampfrichter = $kampfrichterResult->fetch_all(MYSQLI_ASSOC);
$indexedKampfrichter = array_column($kampfrichter, 'name_kampfrichter', 'id_kampfrichter');
$stmtKampfrichter->close();
} else {
$indexedKampfrichter = [];
}
$entriesArray = [];
foreach ($notenChangesEntries as $snce) {
$programmId = $indexedProgramme[$indexedPersonen[$snce['person_id']]['programm']] ?? 0;
$runs = json_decode($IndexedNotenJsonGeraete[$snce['note_bezeichnung_id']], true) ?? [];
$countRuns = (int) $runs[$snce['geraet_id']][$programmId] ?? (int) $runs['default'] ?? 1;
$notenTextRaw = $IndexedNotenBezeichnungen[$snce['note_bezeichnung_id']] ?? 'Unbekannter Notenname';
$notenText = ($countRuns > 1) ? $notenTextRaw . ' (R' . ($snce['run_number'] ?? 1 ). ')' : $notenTextRaw;
$nullstellen = (int) $IndexedNotenNullstellen[$snce['note_bezeichnung_id']] ?? 2;
$value = ($snce['old_value'] !== null) ? number_format((float) $snce['old_value'], $nullstellen) . ' ' . json_decode('"\u2192"') . ' ' . number_format((float) $snce['new_value'], $nullstellen) : number_format((float) $snce['new_value'], $nullstellen);
$entriesArray[$snce['geraet_id']][$indexedPersonen[$snce['person_id']]['start_geraet_id']][] = [
'name' => ($indexedPersonen[$snce['person_id']]['name'] ?? 'Unbekannter Nachname') . ', ' . ($indexedPersonen[$snce['person_id']]['vorname'] ?? 'Unbekannter Vorname'),
'programm' => $indexedPersonen[$snce['person_id']]['programm'] ?? 'Unbekanntes Programm',
'verein' => $indexedPersonen[$snce['person_id']]['verein'] ?? 'Unbekannter Verein',
'note' => $notenText,
'value' => $value,
'kampfrichter' => $indexedKampfrichter[$snce['edited_by']] ?? 'Unbekannter Kampfrichter',
'timestamp' => new DateTime($snce['timestamp'])->format('d.m.Y H:i:s') ?? '---'
];
}
// Load TCPDF
require $baseDir . '/../composer/vendor/autoload.php';
/*
// Optional: load custom font
$fontfile = $baseDir . '/wp-content/uploads/fonts/Inter-Regular.ttf'; // adjust path
if (file_exists($fontfile)) {
$fontname = TCPDF_FONTS::addTTFfont($fontfile, 'TrueTypeUnicode', '', 32);
}
*/
class ProtokollPDF extends TCPDF
{
public $current_year;
public $subAbt;
public $abt;
public $columns;
public $discipline;
public $wkName;
public $marginSide;
public $marginTop;
public $pdfHeight;
public $pdfWidth;
public $headerType;
public $now;
// Page header
public $headerBottomY = 0;
public function Header()
{
$this->SetFillColor(255, 255, 255);
$this->Rect(0, 0, $this->pdfWidth, $this->pdfHeight, 'F');
$arrayTitles = [];
$image_file = $_SERVER['DOCUMENT_ROOT'] . '/intern/img/logo-normal.png';
$this->SetY($this->marginTop);
$this->SetX(($this->marginSide ?? 10));
$this->SetFont('freesans', '', 20);
if ($this->headerType === 'bigHeader') {
$this->Cell(0, 0, 'Protokoll ' . $this->wkName . ' ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$preimageX = $this->GetX();
$preimageY = $this->GetY();
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->SetY($preimageY + 5);
$this->SetX($this->marginSide ?? 10); // Force back to left margin
$this->SetFont('freesans', '', 11);
$this->Cell(0, 6, 'Gereat: ' . $this->discipline, 0, 1, 'L');
$this->Cell(0, 6, 'Abteilung: ' . $this->abt, 0, 1, 'L');
$this->Cell(0, 6, 'Gruppe: ' . $this->subAbt, 0, 1, 'L');
$this->Ln(5);
} else {
$this->Cell(10, 0, 'Protokoll ' . $this->wkName . ' ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$this->SetFont('freesans', '', 11);
$info = "Gerät: {$this->discipline} | Abt: {$this->abt} | Gruppe: {$this->subAbt}";
$this->Cell(10, 6, $info, 0, 1, 'L'); // Align all info to the right while title is on the left
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->Ln(18);
}
$columns = $this->columns;
$startY = $this->GetY();
$this->SetFont('', 'B');
$this->SetX($this->marginSide ?? 10);
foreach ($columns as $col) {
$title = $col['header'];
if (($col['rotate'] ?? false)) {
$x = $this->GetX();
$y = $this->GetY() + 7;
$this->StartTransform();
$this->Rotate(45, $x, $y);
$lineY = $startY + 7;
if (($col['id'] ?? '') === 'gesamt') {
$this->SetLineWidth(0.6);
} else {
$this->SetLineWidth(0.2);
}
$this->Line($x, $lineY, $x + ($col['width_header'] ?? 0), $lineY);
$this->SetLineWidth(0.2);
$this->Cell($col['width_header'] ?? 0, 7, $title ?? '', 0, 0, 'C');
$this->StopTransform();
$this->SetX($x + ($col['max_width'] ?? 0));
} else {
$this->Cell($col['max_width'] ?? 0, 7, $title ?? '', 0, 0, $col['align'] ?? 'L');
}
}
$this->Ln();
$this->headerBottomY = $this->GetY();
}
// Page footer
public function Footer()
{
$this->SetLineWidth(0.6);
$this->Line($this->marginSide ?? 10, $this->headerBottomY, $this->pdfWidth - ($this->marginSide ?? 10), $this->headerBottomY);
$this->SetLineWidth(0.2);
$this->SetY(0);
$this->SetX(0);
$this->SetFillColor(255, 255, 255); // white
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(0);
$this->SetX($this->pdfWidth - ($this->marginSide ?? 10));
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(-15);
$this->SetFont('freesans', 'I', 8);
// 1. Page Number - Centered
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C');
// 2. Timestamp - Force to Left
$this->SetX($this->getMargins()['left']); // Reset to left margin
$this->Cell(0, 10, 'Zeitstempel: ' . $this->now, 0, false, 'L');
// 3. Host - Force to Right (Cell with width 0 and align 'R' handles this)
$this->SetX($this->getMargins()['left']); // Reset again so 'R' alignment works from the start
$this->Cell(0, 10, $_SERVER['HTTP_HOST'], 0, 0, 'R', false, 'https://'.$_SERVER['HTTP_HOST']);
}
}
$pdf = new ProtokollPDF($orientation, 'mm', $format, true, 'UTF-8', false);
$pdfHeight = $pdf->getPageHeight();
$pdfWidth = $pdf->getPageWidth();
$pdf->current_year = $current_year;
$pdf->wkName = $wkName;
$pdf->abt = $abt;
$pdf->marginSide = $marginSide;
$pdf->marginTop = $marginTop;
$pdf->pdfHeight = $pdfHeight;
$pdf->pdfWidth = $pdfWidth;
$pdf->now = $now;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($wkName);
$pdf->SetTitle($wkName . "_Protokoll_" . $abt . "_" . $current_year . ".pdf");
$pdf->SetAutoPageBreak(FALSE);
$pdf->SetFont('freesans', '', 11);
// Define columns dynamically
$columns = [
['id' => 'name', 'header' => 'Name Person', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'name'],
['id' => 'programm', 'header' => 'Programm', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'programm'],
['id' => 'verein', 'header' => 'Verein', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'verein'],
['id' => 'note', 'header' => 'Note', 'align' => 'C', 'flex' => false, 'type' => 'field', 'field' => 'note'],
['id' => 'value', 'header' => 'Wert', 'align' => 'C', 'flex' => false, 'type' => 'field', 'field' => 'value'],
['id' => 'kr', 'header' => 'Kampfrichter/in', 'align' => 'C', 'flex' => true, 'type' => 'field', 'field' => 'kampfrichter'],
['id' => 'timestamp', 'header' => 'Zeitstempel', 'align' => 'C', 'flex' => false, 'type' => 'field', 'field' => 'timestamp'],
];
$padding = 4;
unset($col);
// 1. Calculate initial max widths based on headers
foreach ($columns as &$col) {
$col['width_header'] = $pdf->GetStringWidth($col['header']) + ($padding * 2);
if ($col['type'] !== 'note') {
$col['max_width'] = $col['width_header'];
} else {
$col['max_width'] = 0;
}
}
foreach ($entriesArray as $geratId => $singleGeraet) {
$pdf->discipline = $IndexedGeraete[$geratId] ?? 'Unbekanntes Gerät';
foreach ($singleGeraet as $geratIdStart => $singleStartGeraet) {
$pdf->subAbt = $geratIdStart;
$tcolumns = $columns;
foreach ($singleStartGeraet as $se) {
foreach ($tcolumns as &$col) {
$text = '';
if (isset($se[$col['field']])) {
$text = $se[$col['field']];
}
$width = $pdf->GetStringWidth($text) + ($padding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
}
unset($col);
}
// 4. Distribute Flexible Widths
$tablew = $pdfWidth - (2 * $marginSide); // total table width (A4 landscape in mm minus margins)
$fixedWidth = 0;
$flexCols = [];
foreach ($tcolumns as $col) {
if ($col['flex'] ?? false) {
$flexCols[] = $col['id'];
$fixedWidth += $col['max_width'];
} else {
$fixedWidth += $col['max_width'];
}
}
$effTableWidth = $tablew - $fixedWidth;
if (!empty($flexCols) && $effTableWidth > 0) {
$flexTotalInitial = 0;
foreach ($tcolumns as $col) {
if ($col['flex'] ?? false) $flexTotalInitial += $col['max_width'];
}
foreach ($tcolumns as &$col) {
if ($col['flex'] ?? false) {
$col['max_width'] += ($col['max_width'] / $flexTotalInitial) * $effTableWidth;
}
}
unset($col);
}
$pdf->columns = $tcolumns;
$pdf->headerType = 'bigHeader';
$pdf->AddPage();
$pdf->headerType = '';
// After AddPage(), the header has been drawn
$margin_top = $pdf->headerBottomY;
// Now adjust your margins if needed
$pdf->SetMargins($marginSide, $margin_top, $marginSide); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually
$pdf->SetFont('', '');
$rIndex = 0;
foreach ($singleStartGeraet as $r) {
$pdf->SetTextColor(0, 0, 0);
if ($rIndex % 2 == 0) {
$pdf->SetFillColor(255, 255, 255);
} else {
$pdf->SetFillColor(240, 240, 240);
}
$rowHeight = 10;
foreach ($tcolumns as $col) {
if ($pdf->getY() + $rowHeight > $pdfHeight - $minMarginBottomTable) {
$pdf->addPage();
$margin_top = $pdf->headerBottomY;
$pdf->setY($margin_top);
}
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
}
$startX = $pdf->GetX();
$startY = $pdf->GetY();
$text = strip_tags($r[$col['field']]);
if ($col['type'] === 'field') {
$pdf->MultiCell($col['max_width'], $rowHeight, $text, 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->SetY($startY + $rowHeight); // Move Y manually instead of Ln() to account for rowHeight
$rIndex++;
}
$textanzEintaege = (count($singleStartGeraet) > 1) ? 'Einträge': 'Eintrag';
$pdf->SetFont('', '', 7);
$pdf->Cell(20, 6, count($singleStartGeraet).' '.$textanzEintaege, 0, 0, 'L');
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->Output($wkName . "_Protokoll_" . $abt . "_" . $current_year . ".pdf", 'I');
exit;
@@ -0,0 +1,750 @@
<?php
use TCPDF;
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$wkName = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);
$wkName = ($wkName === null) ? 'Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen' : $wkName;
$normalFontSize = 11;
$marginSide = 10;
$marginTop = 10;
$minMarginBottomTable = 20;
$format = 'A4';
$orientation = 'L';
$programm = trim($_POST['prog'] ?? 'P6A');
$buttontype = trim($_POST['type'] ?? 'downloadRangliste');
$allowedOperations = ['downloadRangliste', 'updateServerRangliste'];
if (!in_array($buttontype, $allowedOperations)) {
header('Content-Type: application/json');
echo json_encode(["success" => false, "message" => "Invalide Operation"]);
exit;
}
$upperprogramm = strtoupper($programm);
$stmt = $mysqli->prepare("SELECT `id`, `name` FROM $db_tabelle_disziplinen ORDER BY start_index ASC");
if (!$stmt->execute()) {
http_response_code(400);
exit;
}
$result = $stmt->get_result();
$geraete = $result->fetch_all(MYSQLI_ASSOC);
$disciplines = array_map(
'strtolower',
array_column($geraete, 'name')
);
$stmt->close();
// Determine current year
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT * FROM $db_tabelle_teilnehmende
WHERE LOWER(programm) = LOWER(?)
AND (bezahltoverride = ? OR (bezahlt = ? OR bezahltoverride = ?))
ORDER BY rang ASC
");
$bezahlt1 = '4';
$bezahlt2 = '4';
$bezahlt3 = '0';
$stmt->bind_param('ssss', $programm, $bezahlt1, $bezahlt2, $bezahlt3);
$stmt->execute();
$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);
// Close statement
$stmt->close();
$programmId = db_get_var($mysqli, "SELECT `id` FROM $db_tabelle_kategorien WHERE LOWER(`programm`) = LOWER(?)", [$programm]);
$cache = require $baseDir . '/../scripts/cache/noten-config-calculating-cache.php';
$ascArrayAnzahlLaeufeJSON = $cache['anzahl_laeufe_json'];
// 1. Get the IDs from the first query results
$turnerinnenIds = array_column($personen, 'id');
$rangNote = intval(db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['rangNote']));
if ($rangNote === 0) {
header('Content-Type: application/json');
echo json_encode(["success" => false, "message" => "Keine Note für Rangvergabe ausgewählt"]);
http_response_code(400);
exit;
}
$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;
if (!empty($turnerinnenIds)) {
// 2. Create a string of placeholders: ?,?,?
$placeholders = implode(',', array_fill(0, count($turnerinnenIds), '?'));
// 3. Prepare the IN statement
$sqlNoten = "SELECT * FROM $db_tabelle_wertungen WHERE person_id IN ($placeholders) AND `wk_id` = ?";
$stmtNoten = $mysqli->prepare($sqlNoten);
// --- FIX STARTS HERE ---
// 1. Combine all values into one flat array for binding
$paramsArray = array_merge($turnerinnenIds, [$current_wk_id]);
// 2. Build the types string
// 'i' for every ID, plus 'i' (or 's') for the year
$types = str_repeat('i', count($turnerinnenIds)) . 's';
// 3. Unpack the combined array into the bind_param function
$stmtNoten->bind_param($types, ...$paramsArray);
// --- FIX ENDS HERE ---
$stmtNoten->execute();
$notenResult = $stmtNoten->get_result();
$notenEntries = $notenResult->fetch_all(MYSQLI_ASSOC);
$stmtNoten->close(); // Close inside the IF to avoid errors if IDs were empty
} else {
$notenEntries = [];
}
$indexedNotenEntries = [];
foreach ($notenEntries as $sn) {
if (!isset($indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']])) {
$indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']] = [];
}
$indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']][$sn['run_number']] = $sn['value'];
}
$orderBestRang = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['orderBestRang']);
$okValuesOrderBestRang = ["ASC", "DESC"];
if (!in_array($orderBestRang, $okValuesOrderBestRang)) {
header('Content-Type: application/json');
echo json_encode(["success" => false, "message" => "Invalide Sortierung der Ränge"]);
http_response_code(400);
exit;
}
$alleNoten = db_select($mysqli, $db_tabelle_wertungstypen, "id, default_value, nullstellen, pro_geraet, geraete_json, zeige_auf_rangliste, groesse_auf_rangliste, name", "zeige_auf_rangliste = ? OR id = ?", ['1', $rangNote]);
$displayedNoten = [];
$alleNotenIndexed = array_column($alleNoten, null, 'id');
foreach ($alleNotenIndexed as $key => $sN) {
if (intval($sN['zeige_auf_rangliste']) === 1) {
$displayedNoten[$key] = $sN;
}
}
$ascArrayDefaultValues = array_column($alleNoten, 'default_value', 'id');
$ascArrayGeraeteJSON = array_column($alleNoten, 'geraete_json', 'id');
foreach ($ascArrayGeraeteJSON as $key => $saagj) {
$ascArrayGeraeteJSON[$key] = json_decode($saagj, true) ?? [];
}
// 1. Initialize the structure for every person
$indexedNotenArray = [];
$sortArray = [];
$notenGeraeteArray = array_merge($geraete, array(array("id" => 0)));
foreach ($personen as $sp) {
$pId = $sp['id'];
foreach ($displayedNoten as $an) {
$nId = $an['id'];
$isProGeraet = (intval($an['pro_geraet']) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$nId] ?? [];
foreach ($notenGeraeteArray as $g) {
$gId = $g['id'];
if ($isProGeraet) {
if ($gId === 0) continue;
} else {
if (!in_array($gId, $allowedGeraete)) continue;
}
$indexedNotenArray[$pId][$gId][$nId] = $indexedNotenEntries[$pId][$nId][$gId] ?? [$an['default_value']];
}
if (intval($nId) === $rangNote) {
$val = $indexedNotenArray[$pId][0][$nId] ?? [$an['default_value']];
$sortArray[$pId] = is_array($val) ? end($val) : $val; // fallback to the last run for sorting if needed, or total should just be one run
}
}
}
$wkWebpageDomain = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['linkWebseite']);
// Load TCPDF
require $baseDir . '/../composer/vendor/autoload.php';
// Optional: load custom font
$fontfile = $baseDir . '/wp-content/uploads/fonts/Inter-Regular.ttf'; // adjust path
if (file_exists($fontfile)) {
$fontname = TCPDF_FONTS::addTTFfont($fontfile, 'TrueTypeUnicode', '', 32);
}
class RanglistePDF extends TCPDF
{
public $current_year;
public $programm;
public $upperprogramm;
public $columns;
public $disciplines;
public $wkName;
public $marginSide;
public $marginTop;
public $pdfHeight;
public $pdfWidth;
public $wkWebpageDomain;
// Page header
public $headerBottomY = 0;
public function Header()
{
$this->SetFillColor(255, 255, 255);
$this->Rect(0, 0, $this->pdfWidth, $this->pdfHeight, 'F');
$arrayTitles = [];
$image_file = $_SERVER['DOCUMENT_ROOT'] . '/intern/img/logo-normal.png';
$this->SetY($this->marginTop);
$this->SetX(($this->marginSide ?? 10));
$this->SetFont('freesans', '', 20);
$this->Cell(0, 0, 'Rangliste ' . $this->wkName . ' ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$preimageX = $this->GetX();
$preimageY = $this->GetY();
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->SetY($preimageY);
$this->SetX($this->marginSide ?? 10); // Force back to left margin
$this->SetFont('freesans', '', 11);
$this->Cell(0, 10, 'Programm: ' . $this->upperprogramm, 0, 1, 'L');
$this->Ln(5);
$columns = $this->columns;
$startY = $this->GetY();
$this->SetFont('', 'B');
$this->SetX($this->marginSide ?? 10);
foreach ($columns as $col) {
$isDuplicate = in_array($col['header'], $arrayTitles);
$title = $isDuplicate ? '' : $col['header'];
if (!$isDuplicate) {
$arrayTitles[] = $col['header'];
}
if (($col['rotate'] ?? false) && !$isDuplicate) {
$x = $this->GetX();
$y = $this->GetY() + 7;
$this->StartTransform();
$this->Rotate(45, $x, $y);
$lineY = $startY + 7;
if (($col['id'] ?? '') === 'gesamt') {
$this->SetLineWidth(0.6);
} else {
$this->SetLineWidth(0.2);
}
$this->Line($x, $lineY, $x + ($col['width_header'] ?? 0), $lineY);
$this->SetLineWidth(0.2);
$this->Cell($col['width_header'] ?? 0, 7, $title ?? '', 0, 0, 'C');
$this->StopTransform();
$this->SetX($x + ($col['max_width'] ?? 0));
} else {
$this->Cell($col['max_width'] ?? 0, 7, $title ?? '', 0, 0, $col['align'] ?? 'L');
}
}
$this->Ln();
$this->headerBottomY = $this->GetY();
}
// Page footer
public function Footer()
{
$this->SetLineWidth(0.6);
$this->Line($this->marginSide ?? 10, $this->headerBottomY, $this->pdfWidth - ($this->marginSide ?? 10), $this->headerBottomY);
$this->SetLineWidth(0.2);
$this->SetY(0);
$this->SetX(0);
$this->SetFillColor(255, 255, 255); // white
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(0);
$this->SetX($this->pdfWidth - ($this->marginSide ?? 10));
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(-15);
$this->SetFont('freesans', 'I', 8);
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M');
$this->SetFont('', '');
$this->Cell(0, 10, ($this->wkWebpageDomain ?? $_SERVER['HTTP_HOST']), 0, 0, 'R', false, 'https://' . ($this->wkWebpageDomain ?? $_SERVER['HTTP_HOST']), 0, false, 'T', 'M');
}
}
$pdf = new RanglistePDF($orientation, 'mm', $format, true, 'UTF-8', false);
$pdfHeight = $pdf->getPageHeight();
$pdfWidth = $pdf->getPageWidth();
$pdf->current_year = $current_year;
$pdf->wkName = $wkName;
$pdf->wkWebpageDomain = $wkWebpageDomain;
$pdf->programm = $programm;
$pdf->upperprogramm = $upperprogramm;
$pdf->marginSide = $marginSide;
$pdf->marginTop = $marginTop;
$pdf->pdfHeight = $pdfHeight;
$pdf->pdfWidth = $pdfWidth;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($wkName);
$pdf->SetTitle($wkName . "_Ergebnisse_" . $programm . "_" . $current_year . ".pdf");
$pdf->SetAutoPageBreak(FALSE);
$pdf->SetFont('freesans', '', 11);
// Define columns dynamically
$columns = [
['id' => 'rang', 'header' => 'Rang', 'align' => 'C', 'flex' => false, 'type' => 'rank'],
['id' => 'name', 'header' => 'Name', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'name'],
['id' => 'vorname', 'header' => 'Vorname', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'vorname'],
['id' => 'geburtsdatum', 'header' => 'Jg.', 'align' => 'C', 'flex' => false, 'type' => 'year', 'field' => 'geburtsdatum'],
['id' => 'verein', 'header' => 'Verein', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'verein'],
];
// Add notes dynamically per device
foreach ($geraete as $g) {
foreach ($displayedNoten as $noteDef) {
$neni = $noteDef['id'];
$isProGeraet = (intval($noteDef['pro_geraet']) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if ($isProGeraet || in_array($g['id'], $allowedGeraete)) {
$columns[] = [
'id' => 'note_' . $g['id'] . '_' . $neni,
'header' => $g['name'],
'align' => 'C',
'flex' => false,
'rotate' => true,
'type' => 'note',
'geraet_id' => $g['id'],
'note_bezeichnung_id' => $neni,
'nullstellen' => $noteDef['nullstellen'] ?? 2,
'font_size' => intval($noteDef['groesse_auf_rangliste']) ?? 0
];
}
}
}
// Add Gesamt (Total) column
$columns[] = [
'id' => 'gesamt',
'header' => $alleNotenIndexed[$rangNote]['name'] ?? 'Total',
'align' => 'C',
'flex' => false,
'rotate' => true,
'type' => 'note',
'geraet_id' => 0,
'note_bezeichnung_id' => $rangNote,
'nullstellen' => $alleNotenIndexed[$rangNote]['nullstellen'] ?? 2,
'font_size' => intval($alleNotenIndexed[$rangNote]['groesse_auf_rangliste']) ?? 0
];
$padding = 4;
unset($col);
// 1. Calculate initial max widths based on headers
foreach ($columns as &$col) {
$col['width_header'] = $pdf->GetStringWidth($col['header']) + ($padding * 2);
if ($col['type'] !== 'note') {
$col['max_width'] = $col['width_header'];
} else {
$col['max_width'] = 0;
}
}
// 2. Fill missing defaults and update max widths based on data
foreach ($personen as $sp) {
$pId = $sp['id'];
foreach ($indexedNotenArray[$pId] as $sG => $currentNoten) {
foreach ($displayedNoten as $noteDef) {
$neni = $noteDef['id'];
if (isset($currentNoten[$neni])) {
$runs = $currentNoten[$neni];
if (is_array($runs)) {
$runsConfig = $ascArrayAnzahlLaeufeJSON[$neni] ?? [];
$runsCount = $runsConfig[$sG][$programmId] ?? $runsConfig[$sG]['all'] ?? $runsConfig["default"] ?? 1;
$defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
for ($r = 1; $r <= $runsCount - 1; $r++) {
if (!isset($runs[$r])) {
$runs[$r] = $defaultValue;
}
}
$indexedNotenArray[$pId][$sG][$neni] = $runs;
ksort($runs);
$maxLenStr = "";
foreach ($runs as $rVal) {
$str = number_format((float)$rVal, $noteDef['nullstellen']);
// Approximate max width by string length, GetStringWidth will do the real check later
if (strlen($str) > strlen($maxLenStr)) {
$maxLenStr = $str;
}
}
$val = $maxLenStr;
} else {
$val = number_format((float)$runs, $noteDef['nullstellen']);
}
} else {
// Logic for "Pro Gerät" vs "Specific/Total"
$isProGeraet = (intval($noteDef['pro_geraet']) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if ($isProGeraet) {
if ($sG === 0) continue;
} else {
if (!in_array($sG, $allowedGeraete)) continue;
}
$defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
$runsConfig = $ascArrayAnzahlLaeufeJSON[$neni] ?? [];
$runsCount = $runsConfig[$sG][$programmId] ?? $runsConfig[$sG]['all'] ?? $runsConfig["default"] ?? 1;
$valArray = [];
$maxLenStr = "";
for ($r = 1; $r <= $runsCount - 1; $r++) {
$valArray[$r] = $defaultValue;
$str = number_format((float)$defaultValue, $noteDef['nullstellen']);
if (strlen($str) > strlen($maxLenStr)) {
$maxLenStr = $str;
}
}
$val = $maxLenStr;
$indexedNotenArray[$pId][$sG][$neni] = $valArray;
if ($neni === $rangNote && $sG === 0) {
$sortArray[$pId] = $defaultValue;
}
}
// Update column width if this note is displayed
foreach ($columns as &$col) {
if ($col['type'] === 'note' && $col['geraet_id'] == $sG && $col['note_bezeichnung_id'] == $neni) {
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
$cpadding = $padding;
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
$cpadding = ($col['font_size'] / $normalFontSize) * $padding;
}
$width = $pdf->GetStringWidth($val) + ($cpadding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
}
unset($col);
}
}
// Also update widths for static fields
foreach ($columns as &$col) {
if ($col['type'] === 'field' || $col['type'] === 'year' || $col['type'] === 'rank') {
$text = '';
if ($col['type'] === 'rank') {
$text = '999'; // Placeholder for rank width
} elseif ($col['type'] === 'year') {
$text = '0000';
} elseif (isset($sp[$col['field']])) {
$text = $sp[$col['field']];
}
$width = $pdf->GetStringWidth($text) + ($padding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
}
}
unset($col);
}
function calculateRanks(array $scoreArray, $direction = 'DESC') {
// 1. Sort the scores while preserving keys
if ($direction === 'DESC') {
arsort($scoreArray); // High scores first
} else {
asort($scoreArray); // Low scores first
}
$ranks = [];
$currentRank = 0;
$lastScore = null;
$skipped = 0;
foreach ($scoreArray as $pId => $score) {
if ($score !== $lastScore) {
$currentRank += ($skipped + 1);
$skipped = 0;
} else {
$skipped++;
}
$ranks[$pId] = $currentRank;
$lastScore = $score;
}
return $ranks;
}
// 3. Finalize Ranks
$ranks = calculateRanks($sortArray, $orderBestRang);
// 4. Distribute Flexible Widths
$tablew = $pdfWidth - (2 * $marginSide); // total table width (A4 landscape in mm minus margins)
$fixedWidth = 0;
$flexCols = [];
foreach ($columns as $col) {
if ($col['flex'] ?? false) {
$flexCols[] = $col['id'];
$fixedWidth += $col['max_width'];
} else {
$fixedWidth += $col['max_width'];
}
}
$effTableWidth = $tablew - $fixedWidth;
if (!empty($flexCols) && $effTableWidth > 0) {
$flexTotalInitial = 0;
foreach ($columns as $col) {
if ($col['flex'] ?? false) $flexTotalInitial += $col['max_width'];
}
foreach ($columns as &$col) {
if ($col['flex'] ?? false) {
$col['max_width'] += ($col['max_width'] / $flexTotalInitial) * $effTableWidth;
}
}
unset($col);
}
$pdf->columns = $columns;
$pdf->disciplines = $disciplines;
$pdf->AddPage();
// After AddPage(), the header has been drawn
$margin_top = $pdf->headerBottomY;
// Now adjust your margins if needed
$pdf->SetMargins($marginSide, $margin_top, $marginSide); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually
$pdf->SetFont('', '');
$rIndex = 0;
usort($personen, function($a, $b) use ($ranks) {
$rankA = $ranks[$a['id']];
$rankB = $ranks[$b['id']];
return $rankA <=> $rankB;
});
foreach ($personen as $r) {
$pdf->SetTextColor(0, 0, 0);
$rang = $ranks[$r['id']];
if ($rang == 1) {
$pdf->SetFillColor(255, 235, 128);
} elseif($rang == 2) {
$pdf->SetFillColor(224, 224, 224);
} elseif($rang == 3) {
$pdf->SetFillColor(230, 191, 153);
} elseif ($rIndex % 2 == 0) {
$pdf->SetFillColor(255, 255, 255); // white
} else {
$pdf->SetFillColor(240, 240, 240); // light gray
}
// Determine row height based on max runs
$maxRuns = 1;
foreach ($columns as $col) {
if ($col['type'] === 'note') {
$runs = $indexedNotenArray[$r['id']][$col['geraet_id']][$col['note_bezeichnung_id']] ?? [];
if (is_array($runs) && count($runs) > $maxRuns) {
$maxRuns = count($runs);
}
}
}
// Base height is 8, add extra height for additional runs. Let's say 4mm per additional line.
// But to keep it centered properly, we will use MultiCell with valign='M' (Middle).
$rowHeight = max(8, $maxRuns * 5 + 3); // 5mm per line
if ($pdf->getY() + $rowHeight >= $pdfHeight - $minMarginBottomTable) {
$pdf->addPage();
$pdf->setX($marginSide);
}
foreach ($columns as $col) {
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
}
$startX = $pdf->GetX();
$startY = $pdf->GetY();
if ($col['id'] === 'rang') {
$pdf->SetFont('', 'B');
$pdf->MultiCell($col['max_width'], $rowHeight, $rang, 'B', 'C', true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
$pdf->SetFont('', '');
} elseif ($col['type'] === 'field') {
$pdf->MultiCell($col['max_width'], $rowHeight, $r[$col['field']], 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
} elseif ($col['type'] === 'year') {
$pdf->MultiCell($col['max_width'], $rowHeight, (new DateTime($r[$col['field']]))->format("Y"), 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
} elseif ($col['type'] === 'note') {
$runs = $indexedNotenArray[$r['id']][$col['geraet_id']][$col['note_bezeichnung_id']] ?? [];
if (!is_array($runs)) {
$runs = [$runs];
} else {
ksort($runs);
}
$numRuns = max(count($runs), 1);
$lineHeight = $rowHeight / $numRuns;
$pdf->SetXY($startX, $startY);
$pdf->Cell($col['max_width'], $rowHeight, '', 0, 0, '', true);
$runIndex = 0;
foreach (array_values($runs) as $rnVal) {
$formatted = number_format((float)$rnVal, $col['nullstellen'] ?? 0);
$currentY = $startY + ($runIndex * $lineHeight);
$pdf->SetXY($startX, $currentY);
$pdf->Cell($col['max_width'], $lineHeight, $formatted, 0, 0, $col['align'], false);
$runIndex++;
}
// Draw the bottom border for the entire row height
$pdf->Line($startX, $startY + $rowHeight, $startX + $col['max_width'], $startY + $rowHeight);
// Reset pointer for the next column
$pdf->SetXY($startX + $col['max_width'], $startY);
if ($col['id'] === 'gesamt') {
// Draw the heavy separator line on the left side of the "Gesamt" column
$pdf->SetLineWidth(0.6);
$pdf->Line($startX, $startY, $startX, $startY + $rowHeight);
$pdf->SetLineWidth(0.2);
}
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->SetY($startY + $rowHeight); // Move Y manually instead of Ln() to account for rowHeight
$rIndex++;
}
$pdf->SetFont('', '', 7);
$textanzturnerinnen = (count($personen) > 1) ? 'Turnerinnen': 'Turnerin';
$pdf->Cell(20, 6, count($personen).' '.$textanzturnerinnen, 0, 0, 'L');
if ($buttontype === 'downloadRangliste') {
$pdf->Output($wkName . "_Ergebnisse_" . $programm . "_" . $current_year . ".pdf", 'I');
exit;
} elseif ($buttontype === 'updateServerRangliste') {
$dir = $baseDir . '/files/ranglisten/';
if (!file_exists($dir)) mkdir($dir, 0755, true);
$localPath = $dir . str_replace("/", "", str_replace(" ", "_", $wkName)) . "_Ergebnisse_" . str_replace("/", "", $programm) . "_" . $current_year . ".pdf";
if (file_exists($localPath)) unlink($localPath);
$pdf->Output($localPath, 'F');
header('Content-Type: application/json');
echo json_encode(["success" => true, "message" => "PDF wurde aktualisiert"]);
exit;
}
@@ -0,0 +1,125 @@
<?php
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
// ---------- Get and sanitize input ----------
$personId = intval($_POST['personId'] ?? 0);
$gereatId = intval($_POST['geraetId'] ?? 0);
if ($gereatId < 1) {
echo json_encode(['success' => false, 'message' => 'Kein Gerät angegeben']);
exit;
}
if ($personId < 1) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$sqlQuery = "SELECT
paf.`audiofile_id`,
af.`file_path`,
af.`file_name`,
tu.`name`,
tu.`vorname`
FROM $db_tabelle_teilnehmende_audiofiles paf
INNER JOIN $db_tabelle_audiofiles af
ON paf.`audiofile_id` = af.`id`
LEFT JOIN $db_tabelle_teilnehmende tu
ON paf.`person_id` = tu.`id`
WHERE paf.`person_id` = ?
AND paf.`geraet_id` = ?
";
$stmt = $mysqli->prepare($sqlQuery);
$stmt->bind_param('ii', $personId, $gereatId);
$stmt->execute();
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
if (count($rows) !== 1 || !isset($rows[0]['file_path']) || $rows[0]['file_path'] === null) {
echo json_encode(['success' => false, 'message' => 'Keine Musik']);
exit;
}
$row = $rows[0];
$folder = realpath($baseDir . '/externe-geraete/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'audio-id-'. $gereatId .'.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
echo json_encode(['success' => false, 'message' => 'Folder not writable.']);
exit;
}
if (!is_file($filepath)){
file_put_contents($filepath, []);
}
$json = [
"audio_path" => $row['file_path'],
"audio_name" => $row['file_name'],
"start" => true,
"person" => [
"name" => $row['name'],
"vorname" => $row['vorname']
]
];
$jsonData = json_encode($json);
// Write file
if (file_put_contents($filepath, $jsonData) === false) {
echo json_encode([
'success' => false,
'message' => 'Failed to write JSON file'
]);
exit;
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'Audio JSON updated successfully'
]);
exit;
@@ -0,0 +1,67 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
$gereatId = intval($_POST['geraetId'] ?? 0);
if ($gereatId < 1) {
echo json_encode(['success' => false, 'message' => 'Kein Gerät angegeben']);
exit;
}
$folder = realpath($baseDir . '/externe-geraete/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'audio-id-'. $gereatId .'.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
echo json_encode(['success' => false, 'message' => 'Folder not writable']);
exit;
}
if (!is_file($filepath)){
file_put_contents($filepath, []);
}
$json = [
"start" => false
];
$jsonData = json_encode($json);
// Write file
if (file_put_contents($filepath, $jsonData) === false) {
echo json_encode([
'success' => false,
'message' => 'Failed to write JSON file: ' . $filepath
]);
exit;
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'JSON updated successfully for '.$discipline,
'disable_musik_button' => true
]);
exit;
@@ -0,0 +1,315 @@
<?php
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$person_id = isset($_POST['personId']) ? intval($_POST['personId']) : 0;
$field_type_id = intval($_POST['fieldTypeId'] ?? 0);
$gereat_id = intval($_POST['gereatId'] ?? 0);
$jahr = isset($_POST['jahr']) ? intval($_POST['jahr']) : 0;
$run_number = isset($_POST['run']) ? intval($_POST['run']) : 1;
if (!isset($_POST['value'])) {
echo json_encode(['success' => false, 'message' => 'Kein Value angegeben']);
exit;
}
$valueNoteUpdate = floatval($_POST['value']);
if ($person_id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Personen-ID']);
exit;
}
if ($jahr < 1) {
echo json_encode(['success' => false, 'message' => 'Invalides Jahr']);
exit;
}
if ($gereat_id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Geraet-ID']);
exit;
}
$geratExistiert = db_get_var($mysqli, "SELECT 1 FROM $db_tabelle_disziplinen WHERE id = ? LIMIT 1", [$gereat_id]);
if (!$geratExistiert) {
echo json_encode(['success' => false, 'message' => 'Invalide Geraet-ID']);
exit;
}
if ($field_type_id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Notentyp-ID']);
exit;
}
$noteConfig = db_select($mysqli, $db_tabelle_wertungstypen, "*", "id = ?", [$field_type_id]);
if (count($noteConfig) !== 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Notentyp-ID']);
exit;
}
$singleNoteConfig = $noteConfig[0];
if ($singleNoteConfig['max_value'] !== null && $valueNoteUpdate > $singleNoteConfig['max_value']) {
echo json_encode(['success' => false, 'message' => "Wert zu hoch (max " . $singleNoteConfig['max_value'].")"]);
exit;
}
if ($singleNoteConfig['min_value'] !== null && $valueNoteUpdate < $singleNoteConfig['min_value']){
echo json_encode(['success' => false, 'message' => "Wert zu niedrig (min " . $singleNoteConfig['min_value'].")"]);
exit;
}
$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;
$oldNote = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_wertungen WHERE `person_id` = ? AND `note_bezeichnung_id` = ? AND `geraet_id` = ? AND `wk_id` = ? AND `run_number` = ?", [$person_id, $field_type_id, $gereat_id, $current_wk_id, $run_number]);
$sql = "INSERT INTO $db_tabelle_wertungen (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `wk_id`, `run_number`)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("siiiii", $valueNoteUpdate, $person_id, $field_type_id, $gereat_id, $current_wk_id, $run_number);
$stmt->execute();
$stmt->close();
$userId = (int) $_SESSION['user_id_kampfrichter'];
$sql = "INSERT INTO $db_tabelle_wertungen_log (`new_value`, `old_value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `wk_id`, `run_number`, `edited_by`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ssiiiiii", $valueNoteUpdate, $oldNote, $person_id, $field_type_id, $gereat_id, $current_wk_id, $run_number, $userId);
$stmt->execute();
$stmt->close();
if ($singleNoteConfig['berechnung_json'] === null) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert"]);
exit;
}
require $baseDir . "/../scripts/string-calculator/string-calculator-functions.php";
$updateNoten = [];
$notenRechner = new NotenRechner();
try {
$abhaenigeRechnungen = json_decode($singleNoteConfig['berechnung_json']);
} catch (Exception $e) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, fehler bei der Berechnung der weiteren Werte"]);
exit;
}
$geraete = db_select($mysqli, $db_tabelle_disziplinen, "id");
$programmName = db_get_var($mysqli, "SELECT `programm` FROM $db_tabelle_teilnehmende WHERE `id` = ?", [$person_id]);
$programmId = db_get_var($mysqli, "SELECT `id` FROM $db_tabelle_kategorien WHERE `programm` = ?", [$programmName]);
// We load the configuration native cached array
$cache = require $baseDir . '/../scripts/cache/noten-config-calculating-cache.php';
$noten = db_select($mysqli, $db_tabelle_wertungen, "`value`, `note_bezeichnung_id`, `geraet_id`, `run_number`", "`person_id` = ? AND `wk_id` = ? AND `value` IS NOT NULL", [$person_id, $current_wk_id]);
$ascArrayDefaultValues = $cache['default_values'];
$ascArrayProGeraet = $cache['pro_geraet'];
$ascArrayGeraeteJSON = $cache['geraete_json'];
$ascArrayAnzahlLaeufeJSON = $cache['anzahl_laeufe_json'];
$ascArrayRechnungen = $cache['rechnungen'];
$indexedNullstellen = $cache['nullstellen'];
$mRunFunctions = [];
foreach ($abhaenigeRechnungen as $saRechnung) {
$sRechnung = $ascArrayRechnungen[$saRechnung[0]] ?? 0;
//var_dump($sRechnung);
$mRunCalc = $notenRechner->checkRunFunctions($sRechnung) ?? false;
if ($mRunCalc) {
$mRunFunctions[] = $saRechnung[0];
}
}
$indexedNotenArray = [];
foreach ($geraete as $g) {
$indexedNotenArray[$g['id']] = [];
}
$indexedNotenArray[0] = [];
foreach ($noten as $sn) {
$indexedNotenArray[$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = ["value" => $sn['value'], "type" => "note"];
}
$requiredNoteIds = [];
$requiredNoteIds[$field_type_id] = true;
foreach ($abhaenigeRechnungen as $saRechnung) {
// Only load dependencies for the specifically connected calculations
$sRechnungStr = $ascArrayRechnungen[$saRechnung[0]] ?? '';
if ($sRechnungStr !== '') {
$ids = $notenRechner->getBenoetigteIdsComplex($sRechnungStr);
foreach ($ids as $idConfig) {
$requiredNoteIds[(int)$idConfig['noteId']] = true;
}
}
}
$alleNotenIds = array_keys($requiredNoteIds);
if (count($mRunFunctions) > 0) {
foreach ($indexedNotenArray as $sG => $siNA) {
foreach ($alleNotenIds as $neni) {
if (!isset($ascArrayDefaultValues[$neni])) continue;
$isProGeraet = (int)($ascArrayProGeraet[$neni] ?? 0);
if ($isProGeraet === 1 && (int)$sG === 0) continue;
if ($isProGeraet !== 1) {
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if (!is_array($allowedGeraete) || !in_array($sG, $allowedGeraete)) continue;
}
$runs = $ascArrayAnzahlLaeufeJSON[$neni][$sG][$programmId] ?? $ascArrayAnzahlLaeufeJSON[$neni][$sG]['all'] ?? $ascArrayAnzahlLaeufeJSON[$neni]["default"] ?? 1;
for ($r = 1; $r <= $runs; $r++) {
if (!isset($indexedNotenArray[$sG][$neni][$r])) {
$indexedNotenArray[$sG][$neni][$r] = ["value" => $ascArrayDefaultValues[$neni], "type" => "default_value"];
}
}
}
}
} else {
foreach ($indexedNotenArray as $sG => $siNA) {
foreach ($alleNotenIds as $neni) {
if (isset($indexedNotenArray[$sG][$neni][$run_number])) continue;
if (!isset($ascArrayDefaultValues[$neni])) continue;
$isProGeraet = (int)($ascArrayProGeraet[$neni] ?? 0);
if ($isProGeraet === 1 && (int)$sG === 0) continue;
if ($isProGeraet !== 1) {
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if (!is_array($allowedGeraete) || !in_array($sG, $allowedGeraete)) continue;
}
$indexedNotenArray[$sG][$neni][$run_number] = ["value" => $ascArrayDefaultValues[$neni], "type" => "default_value"];
}
}
}
// We only want to save the IDs that were actually recalculated
$idsToSave = [];
foreach ($abhaenigeRechnungen as $sRechnung) {
$targetNoteId = $sRechnung[0];
$rechnungType = $sRechnung[1];
$targetRun = $sRechnung[2][0] ?? 'A';
// 1. Initial Filter
if ($rechnungType !== "A" && intval($rechnungType) !== $gereat_id) continue;
$rechnung = $ascArrayRechnungen[$targetNoteId] ?? null;
if ($rechnung === null) {
echo json_encode(['success' => true, 'message' => "Fehler: Rechnung $targetNoteId nicht gefunden"]);
exit;
}
// 2. Determine Target Device ID
$isProGeraet = (intval($ascArrayProGeraet[$targetNoteId] ?? 0) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$targetNoteId] ?? [];
if ($rechnungType === "A" || $isProGeraet || in_array($gereat_id, $allowedGeraete)) {
$targetGeraetKey = $gereat_id;
} else {
$targetGeraetKey = 0;
}
// 3. Calculation Logic
$runsConfig = $ascArrayAnzahlLaeufeJSON[$targetNoteId] ?? [];
$runs = $runsConfig[$targetGeraetKey][$programmId] ?? $runsConfig[$targetGeraetKey]["all"] ?? $runsConfig["default"] ?? 1;
$acrun = min($runs, $run_number);
if (in_array($targetNoteId, $mRunFunctions)) {
$calcResult = $notenRechner->berechneStringComplexRun($rechnung, $indexedNotenArray, $targetGeraetKey, $programmId, $ascArrayAnzahlLaeufeJSON);
} else {
$calcResult = $notenRechner->berechneStringComplex($rechnung, $indexedNotenArray, $targetGeraetKey, $acrun);
}
if (!($calcResult['success'] ?? false)) {
echo json_encode(['success' => true, 'message' => "Rechenfehler in $targetNoteId: " . ($calcResult['value'] ?? '')]);
exit;
}
$acTargetRun = ($targetRun === "A") ? $acrun : intval($targetRun);
// 4. Update State
$val = $calcResult['value'];
$indexedNotenArray[$targetGeraetKey][$targetNoteId][$acTargetRun] = ["value" => $val, "type" => "berechnet"];
$updatedValues[$targetGeraetKey][$targetNoteId][$acTargetRun] = $val;
}
// Prepare the statement once
$sql = "INSERT INTO $db_tabelle_wertungen (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `wk_id`, `run_number`)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
$formatedNoten = [];
$mysqli->begin_transaction();
foreach ($updatedValues as $gereat => $notenArray) {
foreach ($notenArray as $note => $runArray) {
foreach ($runArray as $run => $value) {
$stmt->bind_param("siiiii", $value, $person_id, $note, $gereat, $current_wk_id, $run);
$stmt->execute();
$formatedNoten[$gereat][$note][$run] = number_format($value ,$indexedNullstellen[$note] ?? 2);
}
}
}
$mysqli->commit();
$formatedNoten[$gereat_id][$field_type_id][$run_number] = number_format($valueNoteUpdate ,$indexedNullstellen[$field_type_id] ?? 2);
$stmt->close();
$mysqli->close();
echo json_encode([
'success' => true,
'message' => "Wert aktualisiert, alle Berechnungen durchgeführt",
"noten" => $formatedNoten
]);
@@ -0,0 +1,297 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
// ---------- Get and sanitize input ----------
$id = intval($_POST['personId'] ?? 0);
$initNoteRun = intval($_POST['run'] ?? 0);
$geraetId = intval($_POST['geraetId'] ?? 0);
$dataType = intval($_POST['dataType'] ?? 0);
$jahr = isset($_POST['jahr']) ? preg_replace('/[^0-9]/', '', $_POST['jahr']) : '';
$anfrageType = $_POST['type'] ?? '';
$allowedTypes = ["neu", "start", "result"];
if (!in_array($anfrageType, $allowedTypes)) {
echo json_encode(['success' => false, 'message' => "Operation nicht gestattet."]);
exit;
}
if ($anfrageType !== "start" && ($id < 1 || intval($jahr) < 1)) {
echo json_encode(['success' => false, 'message' => 'Personen ID ist nicht valide.']);
exit;
}
if ($geraetId < 1) {
echo json_encode(['success' => false, 'message' => 'Invalid discipline']);
exit;
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$stmt = $mysqli->prepare("SELECT `name` FROM $db_tabelle_disziplinen WHERE `id` = ? LIMIT 1");
$stmt->bind_param("s", $geraetId);
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$result = $stmt->get_result();
if ($result->num_rows === 0) {
echo json_encode(['success' => false, 'message' => 'Invalid discipline']);
exit;
}
$geraetData = $result->fetch_assoc();
$geraetName = $geraetData['name'];
$stmt->close();
$folder = realpath($baseDir . '/externe-geraete/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'display-id-' . $geraetId . '.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
echo json_encode(['success' => false, 'message' => 'Folder not writable']);
exit;
}
$jsonString = file_get_contents($filepath);
// decode JSON, fallback to empty array if invalid
$oldjson = json_decode($jsonString, true) ?? [];
switch ($anfrageType) {
case "neu":
$stmt = $mysqli->prepare("SELECT `id`, `name`, `vorname`, `programm`, `verein` FROM `$db_tabelle_teilnehmende` WHERE id = ? LIMIT 1");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
if (!$rows || !is_array($rows) || count($rows) !== 1) {
echo json_encode(['success' => false, 'message' => 'Row fetch failed']);
exit;
}
$row = $rows[0];
$data = ["noteLinks" => '',
"noteRechts" => '',
"id" => $id,
"name" => $row['name'],
"vorname" => $row['vorname'],
"programm" => $row['programm'],
"verein" => $row['verein'],
"start" => false
];
$arrayData = $data;
break;
case "start":
if (!array_key_exists("id", $oldjson) || intval($oldjson["id"]) !== $id || !array_key_exists("start", $oldjson)) {
echo json_encode(['success' => false, 'message' => 'Person nicht auf Display!']);
exit;
}
$oldjson["start"] = (bool) $dataType;
$arrayData = $oldjson;
break;
case "result":
// 1. Get IDs and filter out empty values
$noteLinksId = db_get_variable($mysqli, $db_tabelle_var, ['displayIdNoteL']);
$noteRechtsId = db_get_variable($mysqli, $db_tabelle_var, ['displayIdNoteR']);
$programm_id = db_get_var($mysqli, "SELECT p.`id` FROM $db_tabelle_teilnehmende t INNER JOIN $db_tabelle_kategorien p ON p.`programm` = t.`programm` WHERE t.`id` = ?", [$id]) ?? null;
$display_cache = require $baseDir . '/../scripts/cache/display-dependencies.php';
$this_display_cache = $display_cache[$programm_id][$geraetId][$initNoteRun];
$sql_where_values = $this_display_cache['values'];
$sql_where_string = $this_display_cache['SQL'];
$intersect_noten = $this_display_cache['intersect_noten'];
$rankLiveArray = [];
$current_wk_id = (int) db_get_variable($mysqli, $db_tabelle_var, ['current_wk_id']) ?: 0;
if (!empty($sql_where_values) && $sql_where_string !== '') {
$notenDB = db_select($mysqli, $db_tabelle_wertungen, '`value`, `note_bezeichnung_id`, `geraet_id`, `run_number`', "`person_id` = ? AND `wk_id` = ? AND ($sql_where_string)", array_merge([$id, $current_wk_id], $sql_where_values));
$indexedNotenDB = [];
foreach ($notenDB as $sn) {
$indexedNotenDB[$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = $sn;
}
}
$mysqli->begin_transaction();
try {
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_wertungen (`person_id`, `note_bezeichnung_id`, `wk_id`, `geraet_id`, `run_number`, `is_public`, `public_value`) VALUES (?, ?, ?, ?, ?, 1, ?) ON DUPLICATE KEY UPDATE `public_value` = VALUES(`public_value`), `is_public` = 1");
foreach ($intersect_noten as $s_note) {
$n_id = $s_note['n_id'];
$g_id = $s_note['g_id'];
$run = $s_note['r'];
$value = $indexedNotenDB[$g_id][$n_id][$run]['value'] ?? $notenConfig['default_values'][$n_id] ?? 0;
$rankLiveArray[$g_id][$run][$n_id] = number_format($value, $notenConfig['nullstellen'][$n_id] ?? 2);
$stmt->bind_param("iiiiid", $id, $n_id, $current_wk_id, $g_id, $run, $value);
$stmt->execute();
}
$mysqli->commit();
$stmt->close();
} catch (Exception $e) {
$mysqli->rollback();
echo json_encode(['success' => false, 'message' => 'DB Transaction failed Error: ' . $e->getMessage()]);
exit;
}
// Create an array of IDs that actually exist
$validIds = array_filter([$noteLinksId, $noteRechtsId]);
$noten = [];
$notenConfig = [];
if (!empty($validIds)) {
// 2. Fetch Noten (Only if we have IDs to look for)
$placeholders = implode(',', array_fill(0, count($validIds), '?'));
$sqlNoten = "SELECT `value`, `note_bezeichnung_id` FROM $db_tabelle_wertungen
WHERE person_id = ? AND `wk_id` = ? AND `geraet_id` = ? AND run_number = ?
AND `note_bezeichnung_id` IN ($placeholders)";
$stmt = $mysqli->prepare($sqlNoten);
// Combine standard params with our dynamic ID list
$params = array_merge([$id, $current_wk_id, $geraetId, $initNoteRun], $validIds);
$types = str_repeat('s', count($params));
$stmt->bind_param($types, ...$params);
$stmt->execute();
$notenDB = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
$noten = array_column($notenDB, 'value', 'note_bezeichnung_id');
$stmt->close();
// 3. Fetch Config
$sqlConfig = "SELECT `id`, `default_value`, `nullstellen`, `display_string`
FROM $db_tabelle_wertungstypen WHERE `id` IN ($placeholders)";
$stmt = $mysqli->prepare($sqlConfig);
$typesConfig = str_repeat('s', count($validIds));
$stmt->bind_param($typesConfig, ...$validIds);
$stmt->execute();
$notenConfigDB = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
$notenConfig = array_column($notenConfigDB, null, 'id');
$stmt->close();
}
// 4. Helper function to safely format the output without crashing
$formatNote = function($id) use ($noten, $notenConfig) {
if (!$id || !isset($notenConfig[$id])) {
return ""; // Return empty string if ID is not set or not found in DB
}
$conf = $notenConfig[$id];
$val = $noten[$id] ?? $conf['default_value'] ?? 0;
$prec = $conf['nullstellen'] ?? 2;
$display_schema = $conf['display_string'] ?? '${note}';
$formatted_note = number_format((float)$val, (int)$prec, '.', '');
$result = str_ireplace('${note}', $formatted_note, $display_schema);
if ($result === $display_schema) {
return $formatted_note;
}
return $result;
};
// 5. Assign to JSON
$oldjson["noteLinks"] = $formatNote($noteLinksId);
$oldjson["noteRechts"] = $formatNote($noteRechtsId);
$arrayData = $oldjson;
break;
default:
http_response_code(400);
exit;
}
$jsonData = json_encode($arrayData);
// Write file
if (file_put_contents($filepath, $jsonData) === false) {
echo json_encode([
'success' => false,
'message' => 'Failed to write JSON file'
]);
exit;
}
if ($anfrageType === "result") {
echo json_encode([
'success' => true,
'message' => 'JSON updated successfully for ' . $geraetName,
'data' => $arrayData,
'rankLive' => $rankLiveArray,
'nameGeraet' => strtolower($geraetName)
]);
} else {
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'JSON updated successfully for ' . $geraetName,
'data' => $arrayData,
'nameGeraet' => strtolower($geraetName)
]);
}
exit;
@@ -0,0 +1,557 @@
jQuery(document).ready(function($) {
const csrf_token = window.CSDR_TOKEN;
const allowedRanglistenTypes = ['downloadRangliste', 'updateServerRangliste'];
$('.ranglisteExport').on('click', function() {
const $button = $(this);
const progId = $button.data('id');
const fieldType = $button.data('field_type');
if (!allowedRanglistenTypes.includes(fieldType)) {
displayMsg(0, "Dieser Button besitzt eine fehlerhafte Konfiguration");
return;
}
// Visual feedback (optional but recommended)
$button.addClass('opacity50');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_rangliste.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
prog: progId,
type: fieldType
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
// Return both the response and the blob/json promise
// to keep the chain clean, or handle them based on fieldType here.
if (fieldType === 'downloadRangliste') {
return res.blob().then(blob => ({ type: 'blob', data: blob }));
} else {
return res.json().then(json => ({ type: 'json', data: json }));
}
})
.then(result => {
if (result.type === 'blob') {
const url = window.URL.createObjectURL(result.data);
const a = document.createElement('a');
a.href = url;
a.download = "Rangliste.pdf";
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
displayMsg(1, "Rangliste wurde erfolgreich heuntergeladen.");
} else if (result.type === 'json') {
if (result.data.success) {
const $container = $button.closest(".singleAbtDiv");
$container.find(".uploadRanglisteText").addClass("hidden");
$container.find(".updateRanglisteText").removeClass("hidden");
$container.find(".rangliseOnlineSpan").removeClass("hidden");
$container.find(".ranglisteDelete").removeClass("hidden");
displayMsg(1, "Die Rangliste wurde auf dem Server aktualisiert.");
} else {
displayMsg(0, "Fehler beim Aktualiseren der Rangliste.");
}
}
$button.removeClass('opacity50');
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
$button.removeClass('opacity50');
});
});
$('.ranglisteDelete').on('click', function() {
const $button = $(this);
const progId = $button.data('id');
// Visual feedback (optional but recommended)
$button.addClass('opacity50');
const url = '/intern/scripts/kampfrichter/ajax/ajax-delete_rangliste.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
prog: progId
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.json().then(json => ({ type: 'json', data: json }));
})
.then(result => {
if (result.data.success) {
const $container = $button.closest(".singleAbtDiv");
$container.find(".uploadRanglisteText").removeClass("hidden");
$container.find(".updateRanglisteText").addClass("hidden");
$container.find(".rangliseOnlineSpan").addClass("hidden");
$container.find(".ranglisteDelete").addClass("hidden");
displayMsg(1, "Die Rangliste wurde erfolgreich entfernt.");
} else {
displayMsg(0, "Fehler beim Löschen der Rangliste.");
}
$button.removeClass('opacity50');
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
$button.removeClass('opacity50');
});
});
$('.protokollExport').on('click', function() {
const $button = $(this);
const abteilungId = $button.data('abteilung-id');
const now = new Date();
const formattedDate = new Intl.DateTimeFormat('en-CA', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(now).replace(/,/g, '');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_protokoll.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
abteilungId: abteilungId,
date: formattedDate
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.blob().then(blob => ({ type: 'blob', data: blob }));
})
.then(result => {
if (result.type === 'blob') {
const url = window.URL.createObjectURL(result.data);
const a = document.createElement('a');
a.href = url;
a.download = "Protokoll.pdf";
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
displayMsg(1, "Protokoll wurde erfolgreich heuntergeladen.");
}
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
});
});
$('.protokollDeleteEntries').on('click', async function() {
const $button = $(this);
const abt = $button.data('abteilung-id');
if (!await displayConfirmImportant("Möchten Sie wirklich alle Protokolleinträge für diese Abteilung löschen? Diese Aktion kann nicht rückgängig gemacht werden.")) { return; }
const url = '/intern/scripts/kampfrichter/ajax/ajax-delete_protokoll_entries.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
abteilungId: abt
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.json().then(json => ({ type: 'json', data: json }));
})
.then(result => {
if (result.data.success) {
displayMsg(1, "Die Einträge wurden erfolgreich entfernt.");
} else {
displayMsg(0, "Fehler beim Löschen der Einträge.");
}
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
});
});
let activeEdit = null;
$(document).on('click', '.editableValue', function () {
const input = $(this);
// Already editing
if (!input.prop('readonly')) {
return;
}
// Close another active edit
if (activeEdit && activeEdit.get(0) !== input.get(0)) {
cancelEdit(activeEdit);
}
input.data('original', input.val());
input.prop('readonly', false).focus().select();
activeEdit = input;
input.on('keydown.edit', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
saveEdit(input);
}
if (e.key === 'Escape') {
e.preventDefault();
cancelEdit(input);
}
});
input.on('blur.edit', function () {
saveEdit(input);
});
});
function saveEdit(input) {
const originalValue = input.data('original');
const newValue = input.val();
const type = input.data('type');
const discipline = input.data('discipline');
const dataId = input.data('id');
if (newValue === originalValue) {
input.prop('readonly', true);
cleanup(input);
return;
}
input.addClass('is-saving');
$.ajax({
url: '/intern/scripts/kampfrichter/ajax/ajax-update_value_kampfrichter_admin.php',
method: 'POST',
data: {
id: dataId,
field_type: type,
discipline: discipline,
value: newValue
},
success: function () {
input.prop('readonly', true);
const row = input.closest('td');
if (row.length) {
row.css(
'background',
'radial-gradient(circle at bottom right, #22c55e, transparent 55%)'
);
setTimeout(() => row.css('background', ''), 2000);
}
ws.send(JSON.stringify({
type: "KAMPFRICHTER_UPDATE",
payload: {
discipline: discipline,
id: dataId,
val: newValue
}
}));
},
error: function () {
input.val(originalValue).prop('readonly', true);
alert('Speichern fehlgeschlagen');
},
complete: function () {
input.removeClass('is-saving');
cleanup(input);
}
});
}
function cancelEdit(input) {
input.val(input.data('original')).prop('readonly', true);
cleanup(input);
}
function cleanup(input) {
input.off('.edit');
activeEdit = null;
}
let rangNotenArray = JSON.parse(window.RANG_NOTEN_ARRAY) ?? [];
const rangNotenId = window.RANG_NOTE_ID ?? 0;
const rangSort = window.RANG_SORT ?? '';
const selecteddiscipline = window.FREIGABE;
ws.addEventListener("message", event => { // Use 'event' as it's more standard than 'blob'
let msgOBJ;
try {
msgOBJ = JSON.parse(event.data);
} catch (error) {
return;
}
// Ensure it's an UPDATE type (matches your sendToGroup logic)
if (msgOBJ?.type === "UPDATE") {
const data = msgOBJ.payload;
// Check access rights
if (selecteddiscipline === 'A') {
const noten = data.noten;
if (noten[0][rangNotenId][1] !== undefined && rangNotenArray[data.programmId][data.personId] !== undefined) {
rangNotenArray[data.programmId][data.personId] = noten[0][rangNotenId][1];
applyRanks(rankProgramm(rangNotenArray, data.programmId, rangSort), true);
}
}
}
});
function rankProgramm(dataObj, programmId, order = 'DESC') {
if (!dataObj[programmId]) {
console.error(`ID ${programmId} not found in data.`);
return [];
}
const values = dataObj[programmId];
// 1. Transform and Sort
const sorted = Object.entries(values)
.map(([id, val]) => ({ id, val }))
.sort((a, b) => {
// 1. Handle null values first (Always push them to the bottom)
if (a.val === null && b.val === null) return 0;
if (a.val === null) return 1; // Move 'a' to a higher index (down)
if (b.val === null) return -1; // Move 'b' to a higher index (down)
// 2. Normal sorting logic for valid numbers
return order.toUpperCase() === 'ASC'
? a.val - b.val
: b.val - a.val;
});
let lastVal = null;
let lastRank = 0;
let sortedMap = [];
sortedMap[programmId] = sorted.map((item, index) => {
// If current value is different from the last, update rank to current position
if (item.val !== lastVal) {
lastRank = index + 1;
}
lastVal = item.val;
return {
rang: lastRank,
...item
};
});
return sortedMap;
}
function rankAll(dataObj, order = 'DESC') {
return Object.fromEntries(
Object.entries(dataObj).map(([groupKey, values]) => {
const sorted = Object.entries(values)
.map(([id, val]) => ({ id, val }))
.sort((a, b) => {
// 1. Handle null values first (Always push them to the bottom)
if (a.val === null && b.val === null) return 0;
if (a.val === null) return 1; // Move 'a' to a higher index (down)
if (b.val === null) return -1; // Move 'b' to a higher index (down)
// 2. Normal sorting logic for valid numbers
return order.toUpperCase() === 'ASC'
? a.val - b.val
: b.val - a.val;
});
let lastVal = null;
let lastRank = 0;
const ranked = sorted.map((item, index) => {
if (item.val !== lastVal) {
lastRank = index + 1;
}
lastVal = item.val;
return {
rang: lastRank,
...item
};
});
return [groupKey, ranked];
})
);
}
function applyRanks(array, sort = false) {
for (const [key, entries] of Object.entries(array)) {
const $tbody = $(`.customDisplayEditorTable tbody[data-programm-id="${key}"]`);
if ($tbody.length === 0) continue;
for (const [skey, entry] of Object.entries(entries)) {
const $row = $tbody.find(`tr[data-person-id="${entry.id}"]:not(.notpaid)`);
if ($row.length === 0) continue;
$row.attr('data-sort-rank', entry.rang);
const $cell = $row.find('.rangField');
const points = parseFloat(rangNotenArray[key][entry.id]);
const text = !isNaN(points) ? entry.rang : '';
$cell.text(text);
}
if (sort) {
const rows = $tbody.find('tr').get();
rows.sort((a, b) => {
const rankA = parseFloat($(a).attr('data-sort-rank')) || 999;
const rankB = parseFloat($(b).attr('data-sort-rank')) || 999;
return rankA - rankB;
});
$.each(rows, (index, row) => {
$tbody.append(row);
});
}
}
}
applyRanks(rankAll(rangNotenArray, rangSort), true);
window.addEventListener('valueChanged', (e) => {
// Use e.detail to get your object
const { noten, programmId, personId } = e.detail;
if (noten[0][rangNotenId][1] !== undefined && rangNotenArray[programmId][personId] !== undefined) {
rangNotenArray[programmId][personId] = noten[0][rangNotenId][1];
applyRanks(rankProgramm(rangNotenArray, programmId, rangSort), true);
}
});
const $parent = $('.allAbtContainer');
const $children = $parent.children('.shiftedGeraetTable');
$children.sort((a, b) => {
const idA = parseFloat($(a).data('geraet-index'));
const idB = parseFloat($(b).data('geraet-index'));
return idA - idB;
});
$parent.append($children);
const wsMessage = window.WS_MESSAGE || '';
if (wsMessage !== '') {
ws.addEventListener('open', () => {
setTimeout(() => {
ws.send(wsMessage);
}, 300);
});
}
$('.deleteNotenAbt').on('click', async function () {
if (!(await displayConfirmImportant('Alle Noten dieses Programmes wirklich löschen? Diese Aktion ist unumkehrbar!'))) return;
const $btn = $(this);
const programmId = parseInt($btn.data('programm-id') || 0);
$.ajax({
url: '/intern/scripts/kampfrichter/ajax/ajax-delete-noten-turnerinnen.php',
type: 'DELETE',
data: {
csrf_token,
programmId
},
success: function (response) {
if (response.success) {
const $tbody = $btn.closest('.singleAbtDiv').find('tbody');
if ($tbody.length !== 1 || $tbody.data('programm-id') !== programmId) {
location.reload();
return;
}
const $allValues = $tbody.find('.changebleValue:not(.emtyPlaceholder)');
$allValues.each((ind, el) => {
$(el).text('-').addClass('emtyPlaceholder');
updateDependentVisibility($(el));
});
const $allRanks = $tbody.find('.rangField');
$allRanks.each((ind, el) => {
$(el).text('');
});
const $allRows = $tbody.find('tr');
$allRows.each((ind, el) => {
$(el).attr('data-sort-rank', "0");
rangNotenArray[programmId][$(el).attr('data-person-id')] = null;
});
displayMsg(1, 'Noten gelöscht');
} else {
displayMsg(0, response.message || 'Fehler beim Löschen');
}
},
error: function () {
displayMsg(0, 'Fehler beim Löschen');
}
})
});
});
@@ -0,0 +1,856 @@
document.addEventListener('keydown', function (e) {
// Mac = Option, Windows/Linux = Alt
const isOptionOrAlt = e.altKey || e.metaKey;
if (isOptionOrAlt && e.shiftKey && e.key.toLowerCase() === 'f') {
toggleFullscreen();
}
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
}
});
function toggleFullscreen() {
// If not in fullscreen → enter fullscreen
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen()
.catch(err => console.error("Fullscreen error:", err));
}
// If already fullscreen → exit fullscreen
else {
document.exitFullscreen();
}
}
let messagePosArray = [];
const csrf_token = window.CSDR_TOKEN;
const text = document.getElementById('wsInfo');
const rect = document.getElementById('wsInfoRectangle');
let ws;
let firstConnect = true;
let wsOpen = false;
const RETRY_DELAY = 2000; // ms
const WSaccesstype = "kampfrichter";
const WSaccess = window.FREIGABE;
const WSaccessPermission = "W";
const urlAjaxNewWSToken = '/intern/scripts/ajax-create-ws-token.php';
async function fetchNewWSToken(accesstype, access, accessPermission) {
try {
const response = await fetch(urlAjaxNewWSToken, {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
csrf_token,
accesstype,
access,
accessPermission
})
});
if (response.status === 403) {
console.warn("Please Re-Autenithicate. Reloading page...");
location.reload();
return null;
}
if (!response.ok) return null;
const data = await response.json();
return data.success ? data.token : null;
} catch (error) {
console.error("Token fetch failed:", error);
return null;
}
}
async function startWebSocket() {
console.log("Attempting WebSocket connection...");
let token;
if (firstConnect) {
token = window.WS_ACCESS_TOKEN;
} else {
token = await fetchNewWSToken(WSaccesstype, WSaccess, WSaccessPermission);
}
if (!token) {
console.error("No valid token available. Retrying...");
scheduleRetry();
return;
}
try {
ws = new WebSocket(`wss://${window.location.hostname}/ws/?access=token&token=${token}`);
} catch (err) {
console.error("Malformed WebSocket URL", err);
scheduleRetry();
return;
}
ws.onopen = () => {
console.log("WebSocket connected!");
if (!firstConnect) {
displayMsg(1, "Live Syncronisation wieder verfügbar");
}
firstConnect = true;
wsOpen = true;
rect.innerHTML =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#28a745"></circle><path d="M7 12l3 3 7-7" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
requestAnimationFrame(() => {
text.style.opacity = 1;
});
};
ws.onerror = (event) => {
console.error("WebSocket error observed." + JSON.stringify(event));
};
ws.onclose = (event) => {
if (firstConnect) {
displayMsg(0, "Live Syncronisation verloren");
console.log(event);
}
firstConnect = false;
wsOpen = false;
rect.innerHTML =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#dc3545"/><path d="M8 8l8 8M16 8l-8 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
requestAnimationFrame(() => {
text.style.opacity = 1;
});
scheduleRetry();
};
}
function scheduleRetry() {
console.log(`Retrying in ${RETRY_DELAY}ms...`);
setTimeout(startWebSocket, RETRY_DELAY);
}
// Start the initial connection attempt safely
startWebSocket();
function updateRunButtons(targetCount, personId, $container) {
if (targetCount === 0) { return; }
const geraetId = $container.find('.submit-display-result').first().data('geraet-id') || "";
const currentCount = $container.find('.submit-display-result').length;
$container.find('.submit-display-result').removeClass('hidden');
if (targetCount > currentCount) {
for (let i = currentCount + 1; i <= targetCount; i++) {
const buttonHtml = `
<input type="button" class="submit-display-result"
data-person-id="${personId}"
data-geraet-id="${geraetId}"
data-run="${i}"
value="Ergebnis anzeigen (Run ${i})">`;
$container.append(buttonHtml);
}
$container.find('.submit-display-result[data-run="1"]').val('Ergebnis anzeigen (Run 1)');
} else if (targetCount < currentCount) {
for (let i = currentCount; i > targetCount; i--) {
$container.find(`.submit-display-result[data-run="${i}"]`).remove();
}
if (targetCount === 1 && $container.find('.submit-display-result').length === 1) {
$container.find('.submit-display-result').val('Ergebnis anzeigen');
}
}
$container.find('.submit-display-result').each(function() {
$(this).attr('data-person-id', personId);
});
}
$.fn.updateCurrentEdit = function() {
return this.each(function() {
const $input = $(this);
const url = '/intern/scripts/kampfrichter/ajax/ajax-kampfrichter_currentedit.php';
if ($input.attr('data-person-id') === "0"){
$('<form>', {
action: '/intern/kampfrichter',
method: 'post'
})
.append($('<input>', {
type: 'hidden',
name: 'next_subabt',
value: 1
}))
.append($('<input>', {
type: 'hidden',
name: 'next_subabt_submit',
value: '>'
}))
.append($('<input>', {
type: 'hidden',
name: 'csrf_token',
value: csrf_token
}))
.appendTo('body')
.submit();
}
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
editId: $input.attr('data-person-id'),
geraet: $input.attr('data-geraet-id') ?? null
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
$(".current-turnerin-name").css({
'color': '#209200ff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $(".current-turnerin-name").css({
'color': ''
}), 2000);
$(".div_edit_values_user").css("display", "flex");
$(".heading_fv_selturnerin")[0].scrollIntoView();
$(".current-turnerin-name").text(response.titel);
$(".fv_nextturnerin").text(response.nturnerin?.name ?? '');
$(".fv_nextturnerin").val(response.nturnerin?.id ?? 0).attr('data-person-id', response.nturnerin?.id ?? 0);
$(".submit-display-turnerin").css("opacity", "1");
$(".submit-display-start").css("opacity", "1");
$(".submit-display-result").css("opacity", "1");
const $editAllDiv = $('.div_edit_values_all_gereate');
const noten = response.noten;
const personId = response.id;
const programmId = response.programm_id;
$editAllDiv.find(`.submit-display-turnerin`).closest('.all_vaules_div').addClass('hidden');
// 1. Loop directly through the 'noten' object
for (const [geraetId, disciplineData] of Object.entries(noten)) {
// Find the specific DOM wrapper for this Geraet using the outer div
// Assuming your PHP renders the tables with the correct geraetId on the button
const $disciplineWrapper = $editAllDiv.find(`.submit-display-turnerin[data-geraet-id="${geraetId}"]`).closest('.all_vaules_div');
if ($disciplineWrapper.length === 0) continue;
$disciplineWrapper.removeClass('hidden');
// --- UPDATE GENERAL BUTTONS FOR THIS GERAET ---
$disciplineWrapper.find(".submit-display-turnerin, .submit-display-start").attr({
'data-person-id': personId,
'data-geraet-id': geraetId
});
$disciplineWrapper.find(".submit-musik-start, .submit-musik-stopp").attr({
'data-id': personId,
'data-geraet': geraetId
});
// 2. Identify master containers for this specific discipline
const $masterContainer = $disciplineWrapper.find('.singleNotentable').first();
const $displayresultDiv = $disciplineWrapper.find('.div-submit-display-result');
$masterContainer.find('.note-container').addClass('hidden');
// 3. CLEANUP: Remove previously generated runs and buttons
$disciplineWrapper.find('.singleNotentable').not(':first').remove();
$displayresultDiv.find('.submit-display-result').not(':first').remove();
const $originalResultBtn = $displayresultDiv.find('.submit-display-result').first();
$displayresultDiv.find('.submit-display-result').addClass('hidden');
const runKeys = Object.keys(disciplineData).sort((a, b) => a - b);
const totalRuns = runKeys.length;
// 4. Process each Run in the data
runKeys.forEach(runNum => {
const runInt = parseInt(runNum);
let $currentRunContainer;
if (runInt === 1) {
$currentRunContainer = $masterContainer;
} else {
// CLONE the entire container for Run 2, 3, etc.
$currentRunContainer = $masterContainer.clone();
$currentRunContainer.addClass(`run-container-block run-${runNum}`);
$currentRunContainer.insertAfter($disciplineWrapper.find('.singleNotentable').last());
}
// 5. Update all Tables and Inputs inside this Run Container
for (const [noteId, value] of Object.entries(disciplineData[runNum])) {
const $table = $currentRunContainer.find(`.note-container[data-note-id="${noteId}"]`);
$table.removeClass('hidden');
// Update Header to show Run Number
if (runInt > 1) {
const $header = $table.find('.note-name-header');
if (!$header.find('.rm-tag').length) {
$header.append(` <span class="rm-tag" style="font-size: 0.8em;">(R${runNum})</span>`);
}
}
// Update Input attributes and value
const $input = $table.find('input');
$input.attr({
'data-run': runNum,
'data-person-id': personId,
'data-geraet-id': geraetId,
'data-programm-id': programmId
}).val(value ?? '');
}
// 6. Remove tables cloned from Run 1 that don't exist in Run 2+
if (runInt > 1) {
$currentRunContainer.find('input[data-run="1"]').closest('.note-container').remove();
}
});
// Ensure the UI script tracking the buttons is updated last
updateRunButtons(totalRuns, personId, $displayresultDiv);
}
//$(".submit-display-result").attr('data-person-id', response.id);
} else {
displayMsg(0, response.message);
$input.css({
'background-color': '#f8d7da',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
console.error('AJAX fetch error:', err);
});
});
};
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
jQuery(document).ready(function($) {
$('.editTurnerin').on('click', function() {
$(this).updateCurrentEdit();
});
const $ajaxInputDiv = $('.div_edit_values_all_gereate');
$ajaxInputDiv.on('change', '.ajax-input', function(e) {
const $input = $(this);
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_value_kampfrichter.php`;
personId = $input.attr('data-person-id');
fieldTypeId = $input.attr('data-field-type-id');
programmId = $input.attr('data-programm-id');
gereatId = $input.attr('data-geraet-id');
runNum = $input.attr('data-run') || 1;
jahr = window.AKTUELLES_JAHR;
value = $input.val();
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
personId: personId,
fieldTypeId: fieldTypeId,
gereatId: gereatId,
run: runNum,
jahr: jahr,
value: value
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
let objValues = [];
const rowId = $input.attr('data-id');
$input.css({"color": "#0e670d", "font-weight": "600"});
setTimeout(() => $input.css({'color': '', "font-weight": ""}), 2000);
const noten = response.noten;
for (const [keyG, noteGroup] of Object.entries(noten)) {
for (const [key, runGroup] of Object.entries(noteGroup)) {
for (const [run, value] of Object.entries(runGroup)) {
const $elements = $(`.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyG}"][data-person-id="${personId}"][data-run="${run}"]`);
$elements.each((ind, el) => {
const $el = $(el);
if ($el.is('input')) {
$el.val(value ?? '');
} else {
const isNullable = ($el.attr('data-noten-nullable') ?? 'false') === 'true';
const floatValZero = parseFloat(value.replace(",", ".").replace("'", "")) === 0.0;
let hasValue = false;
if (isNullable && floatValZero || value === '') {
$el.text('-').addClass('emtyPlaceholder');
} else {
$el.text(value).removeClass('emtyPlaceholder');
hasValue = true;
}
updateDependentVisibility($el, hasValue);
}
});
}
}
}
if (selecteddiscipline === 'A') {
const event = new CustomEvent('valueChanged', {
detail: {
noten: noten,
programmId: programmId,
personId: personId
}
});
window.dispatchEvent(event);
}
if (wsOpen) {
ws.send(JSON.stringify({
type: "KAMPFRICHTER_UPDATE",
payload: {
discipline: window.FREIGABE,
gereatId: gereatId,
personId: personId,
programmId: programmId,
jahr: jahr,
noten: noten
}
}));
}
} else {
// Flash red on error
$input.css({'color': '#ff6a76ff'});
displayMsg(0, response.message || 'Unknown error');
console.error(response.message || 'Unknown error');
}
})
.catch(err => {
$input.css({'color': '#670d0d'});
console.error('AJAX fetch error:', err);
});
});
$('.submit-display-turnerin').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
fetch(url,{
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
personId: $input.attr('data-person-id'),
geraetId,
jahr: window.AKTUELLES_JAHR,
type: "neu"
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
geraetId,
data: response.data
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
displayMsg(1, 'Neue Person wird angezigt');
$input.css('opacity', 0.5);
} else {
displayMsg(0, response.message);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
displayMsg(0, 'AJAX fetch error:' + err);
console.error('AJAX fetch error:', err);
});
});
$('.submit-display-start').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
const personId = $input.attr('data-person-id')
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
const dataType = $input.attr('data-type');
fetch(url,{
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
geraetId,
personId,
dataType: dataType,
type: "start"
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
geraetId,
data: response.data
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
if (dataType == 1) {
displayMsg(1, 'Start freigegeben');
} else if (dataType == 0) {
displayMsg(1, 'Startfreigabe entzogen');
}
} else {
displayMsg(0, response.message);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
displayMsg(0, 'AJAX fetch error:' + err);
console.error('AJAX fetch error:', err);
});
});
$('.submit-musik-start').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_start_musik.php`;
fetch(url,{
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
personId: $input.attr('data-id'),
geraetId
})
})
.then(res => res.json())
.then(response => {
if (wsOpen) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {
audioDiscipline: geraetId
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
if (response.success) {
displayMsg(1, 'Musik wird abgespielt werden');
} else {
displayMsg(0, 'Error: ' + response.message);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
displayMsg(0, 'AJAX fetch error:' + err);
console.error('AJAX fetch error:', err);
});
});
$('.submit-musik-stopp').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_stopp_musik.php`;
fetch(url,{
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
geraetId
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
if (wsOpen) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {
audioDiscipline: geraetId
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
displayMsg(1, 'Musik wird gestoppt werden');
} else {
alert('Error: ' + response.message);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
displayMsg(0, 'AJAX fetch error:' + err);
console.error('AJAX fetch error:', err);
});
});
$('.div-submit-display-result').on('click', '.submit-display-result', function() {
$input = $(this);
const geraetId = $input.attr('data-geraet-id');
const personId = $input.attr('data-person-id');
// Build the URL with GET parameters safely
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
fetch(url,{
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
personId,
geraetId,
run: $input.attr("data-run"),
jahr: window.AKTUELLES_JAHR,
type: "result"
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraetId,
geraet: response.nameGeraet,
data: response.data
}
}));
ws.send(JSON.stringify({
type: "UPDATE_RANKLIVE_SCORE",
payload: {
geraetId,
personId,
jahr: window.AKTUELLES_JAHR,
rankLive: response.rankLive
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
$input.css('opacity', 0.5);
displayMsg(1, 'Resultat wird angezeigt');
} else {
alert('Error: ' + response.message);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
displayMsg(0, 'AJAX fetch error:' + err);
console.error('AJAX fetch error:', err);
});
});
});
function updateDependentVisibility($changedEl, hasValue = false) {
const uid = $changedEl.attr('data-uid');
if (uid === undefined || uid === null) return;
const $row = $changedEl.closest('tr');
if ($row.length !== 1) return;
const isVisible = $changedEl.hasClass('emtyPlaceholder');
const doesExist = $changedEl.hasClass('nonExistentEl');
const $linkedEls = $row.find(`.singleValueSpan[data-linked-el-uid="${uid}"]`);
$linkedEls.each((ind, el) => {
const $linkedEl = $(el);
if (!hasValue) {
hasValue = $linkedEl.hasClass('changebleValue') && $linkedEl.text().trim() !== '-';
}
$linkedEl.toggleClass('emtyPlaceholder', (isVisible && !hasValue)).toggleClass('nonExistentEl', doesExist);
const linkedElUid = $linkedEl.data('linked-el-uid');
const $subLinkedEls = $row.find(`.singleValueSpan[data-linked-el-uid="${linkedElUid}"]`);
if ($subLinkedEls.length > 0) updateDependentVisibility($linkedEl);
});
}
function initConditionalVisibility() {
const $table = $('table.customDisplayEditorTable');
const $rows = $table.find('tr');
$rows.each((ind, el) => {
const $row = $(el);
$row.find('.singleValueSpan[data-linked-el-uid]').each(function() {
const linkedUid = $(this).data('linked-el-uid');
const $linkedEl = $row.find(`.singleValueSpan[data-uid="${linkedUid}"]`);
if ($linkedEl.length === 1) {
updateDependentVisibility($linkedEl);
}
});
})
}
initConditionalVisibility();
initConditionalVisibility();
const selecteddiscipline = window.FREIGABE;
ws.addEventListener("message", event => { // Use 'event' as it's more standard than 'blob'
let msgOBJ;
try {
msgOBJ = JSON.parse(event.data);
} catch (error) {
return;
}
// Ensure it's an UPDATE type (matches your sendToGroup logic)
if (msgOBJ?.type === "UPDATE") {
const data = msgOBJ.payload;
// Check access rights
if (data.discipline === selecteddiscipline.toLowerCase() || selecteddiscipline === 'A') {
const noten = data.noten;
for (const [keyG, noteGroup] of Object.entries(noten)) {
for (const [key, runGroup] of Object.entries(noteGroup)) {
for (const [run, value] of Object.entries(runGroup)) {
// Select all matching elements (inputs and spans)
const $elements = $(`.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyG}"][data-person-id="${data.personId}"][data-run="${run}"]`);
$elements.each((ind, el) => {
const $el = $(el);
if ($el.is('input')) {
$el.val(value ?? '');
// Visual feedback: Flash color
const nativeEl = $el[0];
nativeEl.style.transition = 'color 0.3s';
nativeEl.style.color = '#008e85';
setTimeout(() => {
nativeEl.style.color = '';
}, 1000);
} else {
const isNullable = ($el.attr('data-noten-nullable') ?? 'false') === 'true';
const floatValZero = parseFloat(value.replace(",", ".").replace("'", "")) === 0.0;
let hasValue = false;
if (isNullable && floatValZero || value === '') {
$el.text('-').addClass('emtyPlaceholder');
} else {
$el.text(value).removeClass('emtyPlaceholder');
hasValue = true;
}
updateDependentVisibility($el, hasValue);
$el.removeClass('updated');
$el.addClass('updated');
setTimeout(() => {
$el.removeClass('updated');
}, 2000);
}
});
}
}
}
}
}
});
@@ -0,0 +1,185 @@
<?php
use Dotenv\Dotenv;
header('Content-Type: application/json');
date_default_timezone_set('Europe/Zurich');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$link_expires_at = $_POST['linkExpiresAt'] ?? '';
if ($link_expires_at === '' || strtotime($link_expires_at) === false) {
echo json_encode(['success' => false, 'message' => 'Ungültiges Ablaufdatum']);
http_response_code(400);
exit;
}
if (strtotime($link_expires_at) > strtotime('+8 days')) {
echo json_encode(['success' => false, 'message' => 'Datum liegt zu weit in der Zukunft']);
http_response_code(400);
exit;
}
if (strtotime($link_expires_at) < time()) {
echo json_encode(['success' => false, 'message' => 'Datum liegt in der Vergangenheit']);
http_response_code(400);
exit;
}
$editor_id = $_SESSION['user_id_wk_leitung'];
$plain = trim($_POST['password'] ?? null);
$username = trim($_POST['username'] ?? null);
$namePerson = htmlspecialchars(trim($_POST['namePerson'] ?? null));
$freigaben = $_POST['freigaben'] ?? [];
$freigabenTrainer = $_POST['freigabenTrainer'] ?? [];
$freigabenKampfrichter = $_POST['freigabenKampfrichter'] ?? [];
if (!is_array($freigaben)) {
$freigaben = [];
}
if (!is_array($freigabenTrainer)) {
$freigabenTrainer = [];
}
if (!is_array($freigabenKampfrichter)) {
$freigabenKampfrichter = [];
}
$array = [
'types' => $freigaben,
'freigabenTrainer' => $freigabenTrainer,
'freigabenKampfrichter' => $freigabenKampfrichter
];
// Store as proper JSON string
$freigabe_store = json_encode($array);
$hash = null;
$cipher_store = null;
if ($plain != null) {
// Hash for login
$hash = password_hash($plain, PASSWORD_ARGON2ID);
require $baseDir . '/../composer/vendor/autoload.php';
$envFile = realpath($baseDir . '/../config/.env.pw-encryption-key');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.pw-encryption-key');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
// Encrypt for display
$iv_length = openssl_cipher_iv_length('aes-256-cbc');
$iv = random_bytes($iv_length);
$encrypted = openssl_encrypt($plain, 'aes-256-cbc', $_ENV['PW_ENCRYPTION_KEY'], 0, $iv);
$cipher_store = base64_encode($iv . $encrypted);
}
$created_at = date('Y-m-d H:i:s');
$updated_at = $created_at;
$stmt = $mysqli->prepare(
"INSERT INTO {$db_tabelle_intern_benutzende}
(username, name_person, password_hash, password_cipher, freigabe, created_at, updated_at, edited_by)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
);
$stmt->bind_param(
"sssssssi",
$username,
$namePerson,
$hash,
$cipher_store,
$freigabe_store,
$created_at,
$updated_at,
$editor_id
);
$updated = $stmt->execute();
if (!$updated) {
echo json_encode(['success' => false, 'message' => 'DB Error']);
exit;
}
$new_id = $mysqli->insert_id;
db_delete($mysqli, $db_tabelle_einmal_links, ['user_id' => $new_id]);
$typeOp = "create_profile";
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_einmal_links (user_id, `type`, `expires_at`) VALUES (?, ?, ?)");
$stmt->bind_param("iss", $new_id, $typeOp, $link_expires_at);
if (!$stmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Failed to create OTL record']);
exit;
}
$row_id = $stmt->insert_id;
$stmt->close();
// Now fetch the auto-generated URL
$url = db_get_var($mysqli, "SELECT url FROM $db_tabelle_einmal_links WHERE id = ? LIMIT 1", [$row_id]);
if (!$url) {
echo json_encode(['success' => false, 'message' => 'Could not fetch generated URL']);
exit;
}
echo json_encode(['success' => true, 'url' => $url, 'expiresAt' => $link_expires_at]);
@@ -0,0 +1,72 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
date_default_timezone_set('Europe/Zurich');
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
$id = intval($_POST['user_id'] ?? 0);
$typeOp = trim($_POST['type'] ?? '');
$allowedTypesOp = ['login', 'pwreset'];
if (!in_array($typeOp, $allowedTypesOp)) {
echo json_encode(['success' => false, 'message' => 'Operation nicht gestattet']);
exit;
}
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'No valid ID']);
exit;
}
// Delete old OTL links for this user (recommended)
db_delete($mysqli, $db_tabelle_einmal_links, ['user_id' => $id]);
$timestamp_one_day = strtotime('+1 days');
$link_expires_at = date("Y-m-d H:i:s", $timestamp_one_day);
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_einmal_links (user_id, `type`, `expires_at`) VALUES (?, ?, ?)");
$stmt->bind_param("iss", $id, $typeOp, $link_expires_at);
if (!$stmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Failed to create OTL record']);
exit;
}
$row_id = $stmt->insert_id;
$stmt->close();
$url = db_get_var($mysqli, "SELECT url FROM $db_tabelle_einmal_links WHERE id = ? LIMIT 1", [$row_id]);
if (!$url) {
echo json_encode(['success' => false, 'message' => 'Could not fetch generated URL']);
exit;
}
echo json_encode(['success' => true, 'url' => $url, 'expiresAt' => $link_expires_at]);
@@ -0,0 +1,40 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$id = intval($_POST['field_id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
exit;
}
db_delete($mysqli, $db_tabelle_intern_benutzende, ['id' => $id]);
db_delete($mysqli, $db_tabelle_einmal_links, ['user_id' => $id]);
echo json_encode(['success' => true, 'message' => "Benutzer $id erfolgreich gelöscht.", 'id' => $id]);
exit;
@@ -0,0 +1,50 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
require_once $baseDir . '/../scripts/delete_request_type_functions.php';
parse_input_to_delete();
verify_delete_csrf($_DELETE);
$id = (int) ($_DELETE['id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'No valid ID']);
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
$entrys = db_select($mysqli, $db_tabelle_vereine, "1", '`id` = ?', [$id]);
if (count($entrys) < 1) {
echo json_encode(['success' => true, 'message' => 'Verein bereits gelöscht']);
http_response_code(410);
exit;
}
db_delete($mysqli, $db_tabelle_vereine, ['id' => $id]);
echo json_encode(['success' => true, 'message' => 'Verein gelöscht']);
http_response_code(200);
@@ -0,0 +1,84 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$value = isset($_POST['value']) ? preg_replace('/[^a-zA-Z0-9\s\-"]/u', '', $_POST['value']) : '';
if (!$value || $value === ''){
echo json_encode(['success' => false, 'message' => 'No input']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$stmt = $mysqli->prepare("INSERT INTO `$db_tabelle_kategorien` (programm) VALUES (?)");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical DB ERROR']);
exit;
}
$stmt->bind_param("s", $value);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
// Fetch all rows
$query2 = "SELECT * FROM `$db_tabelle_kategorien` ORDER BY programm ASC";
$programme = $mysqli->query($query2);
if (!$programme) {
echo json_encode(['success' => false, 'message' => 'Select failed']);
exit;
}
$output = [];
while ($entry = $programme->fetch_assoc()) {
$output[] = [
'id' => $entry['id'],
'programm' => $entry['programm'],
'aktiv' => intval($entry['aktiv']),
'preis' => floatval($entry['preis']),
'orderIndex' => intval($entry['order_index'])
];
}
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
// Return JSON
echo json_encode([
'success' => true,
'output' => $output,
'message' => $value.' hinzugefügt'
]);
exit;
@@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
require_once $baseDir . '/../scripts/delete_request_type_functions.php';
parse_input_to_delete();
verify_delete_csrf($_DELETE);
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$id = (int) ($_DELETE['id'] ?? 0);
if ($id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
http_response_code(400);
exit;
}
$entrys = db_select($mysqli, $db_tabelle_kategorien, "programm", '`id` = ?', [$id]);
if (count($entrys) !== 1) {
echo json_encode(['success' => true, 'message' => 'Alters-/ Leistungsklasse bereits gelöscht']);
http_response_code(410);
exit;
}
$bezeichnung = $entrys[0]['programm'];
db_delete($mysqli, $db_tabelle_kategorien, ['id' => $id]);
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
echo json_encode(['success' => true, 'message' => $bezeichnung.' gelöscht']);
http_response_code(200);
exit;
@@ -0,0 +1,193 @@
<?php
use Dotenv\Dotenv;
ini_set('display_errors', 1);
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$allowed_update_types = ['all', 'no_password', 'new_user'];
$update_type = $_POST['updateType'] ?? '';
if (!in_array($update_type, $allowed_update_types)) {
echo json_encode(['success' => false, 'message' => 'Unbekannte Aktion']);
http_response_code(400);
exit;
}
$editor_id = (int) $_SESSION['user_id_wk_leitung'];
if ($update_type === 'new_user') {
$stmt = $mysqli->prepare(
"INSERT INTO {$db_tabelle_intern_benutzende}
(created_at, updated_at, edited_by, login_active)
VALUES (?, ?, ?, ?)"
);
$loginActive = 1;
$created_at = date('Y-m-d H:i:s');
$updated_at = $created_at;
$stmt->bind_param(
"ssis",
$created_at,
$updated_at,
$editor_id,
$loginActive
);
$updated = $stmt->execute();
$new_id = $mysqli->insert_id;
echo json_encode(['success' => true, 'message' => 'Neuer Benutzer wurde erfolgreich erstellt', 'id' => $new_id]);
http_response_code(200);
exit;
}
$id = (int) ($_POST['personId'] ?? 0);
if ($id < 1 && $update_type !== 'new_user') {
echo json_encode(['success' => false, 'message' => 'Die angegebene ID ist nicht valide']);
http_response_code(400);
exit;
}
$plain_password = trim($_POST['password'] ?? '');
if ($plain_password === '' && $update_type !== 'no_password') {
echo json_encode(['success' => false, 'message' => 'Kein Passwort angegeben']);
http_response_code(422);
exit;
}
if ($update_type !== 'no_password' && strlen($plain_password) < 6) {
echo json_encode(['success' => false, 'message' => 'Das Passwort muss mindestens 6 Zeichen enthalten']);
http_response_code(400);
exit;
}
$username = htmlspecialchars(trim($_POST['username'] ?? ''));
if ($username === '') {
echo json_encode(['success' => false, 'message' => 'Keinen Benutzernamen angegeben']);
http_response_code(422);
exit;
}
$namePerson = htmlspecialchars(trim($_POST['namePerson'] ?? ''));
$freigaben = $_POST['freigaben'] ?? [];
$freigabenTrainer = $_POST['freigabenTrainer'] ?? [];
$freigabenKampfrichter = $_POST['freigabenKampfrichter'] ?? [];
if (!is_array($freigaben)) {
$freigaben = [];
}
if (!is_array($freigabenTrainer)) {
$freigabenTrainer = [];
}
if (!is_array($freigabenKampfrichter)) {
$freigabenKampfrichter = [];
}
$array = [
'types' => $freigaben,
'freigabenTrainer' => $freigabenTrainer,
'freigabenKampfrichter' => $freigabenKampfrichter
];
// Store as proper JSON string
$freigabe_store = json_encode($array);
if ($update_type !== 'no_password') {
$hash = password_hash($plain_password, PASSWORD_ARGON2ID);
require $baseDir . '/../composer/vendor/autoload.php';
$envFile = realpath($baseDir . '/../config/.env.pw-encryption-key');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.pw-encryption-key');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
$iv_length = openssl_cipher_iv_length('aes-256-cbc');
$iv = random_bytes($iv_length);
$encrypted = openssl_encrypt($plain_password, 'aes-256-cbc', $_ENV['PW_ENCRYPTION_KEY'], 0, $iv);
$cipher_store = base64_encode($iv . $encrypted);
}
if ($update_type === 'no_password') {
$updated = db_update($mysqli, $db_tabelle_intern_benutzende, [
'username' => $username,
'name_person' => $namePerson,
'freigabe' => $freigabe_store,
'updated_at' => date('Y-m-d H:i:s'),
'edited_by' => $editor_id
], ['id' => $id]);
} else {
$updated = db_update($mysqli, $db_tabelle_intern_benutzende, [
'password_hash' => $hash,
'password_cipher' => $cipher_store,
'username' => $username,
'name_person' => $namePerson,
'freigabe' => $freigabe_store,
'updated_at' => date('Y-m-d H:i:s'),
'edited_by' => $editor_id
], ['id' => $id]);
}
if ($updated === false) {
echo json_encode(['success' => false, 'message' => 'DB Error']);
http_response_code(500);
exit;
}
echo json_encode(['success' => true, 'message' => $username.' wurde erfolgreich aktualisiert.']);
http_response_code(200);
exit;
@@ -0,0 +1,69 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
// ---------- Get and sanitize input ----------
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$value = isset($_POST['value']) ? floatval($_POST['value']) : 0;
$type = isset($_POST['type']) ? $_POST['type'] : '';
$allowedTypes = ['preis', 'order_index', 'aktiv'];
if (!in_array($type, $allowedTypes, true)) {
echo json_encode(['success' => false, 'message' => 'Invalid type']);
http_response_code(400);
exit;
}
if ($id < 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$query = "UPDATE `$db_tabelle_kategorien` SET $type = '$value' WHERE id = $id";
$result = $mysqli->query($query);
if (!$result) {
echo json_encode(['success' => false, 'message' => 'Update failed']);
exit;
}
if ($type === 'aktiv') {
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'Aktualisiert'
]);
exit;
@@ -0,0 +1,67 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$id = intval($_POST['field_id']) ?? '';
$verein = htmlspecialchars(trim($_POST['verein'] ?? ''));
if (!isset($id) || !is_int($id) || !isset($verein)){
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
exit;
}
if ($id > 0) {
$updated = db_update($mysqli, $db_tabelle_vereine, [
'verein' => $verein,
], ['id' => $id]);
} else {
$stmt = $mysqli->prepare(
"INSERT INTO {$db_tabelle_vereine}
(verein)
VALUES (?)"
);
$stmt->bind_param(
"s",
$verein
);
$updated = $stmt->execute();
}
if ($updated !== false) {
if ($id == 0) { // new user
$new_id = $mysqli->insert_id;
echo json_encode(['success' => true, 'message' => $verein.' wurde erfolgreich erstellt.', 'id' => $new_id]);
} else {
echo json_encode(['success' => true, 'message' => $verein.' wurde erfolgreich aktualisiert.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'DB Error']);
}
@@ -0,0 +1,51 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$slug = $_POST['slug'] ?? '';
$allowed_slugs = ['rankLive-overview', 'rankLive-geraet', 'kampfrichter-admin', 'kampfrichter-geraet', 'rangliste'];
if (!in_array($slug, $allowed_slugs)){
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
$_SESSION['current_ranklive_slug'] = $slug;
$db_res = db_select($mysqli, $db_tabelle_tabellen_konfiguration, '`json`', '`type_slug` = ?', [$slug], '', 1);
$res = $db_res[0]['json'] ?? '';
// Return JSON
echo json_encode([
'success' => true,
'data' => $res,
'message' => 'gespeichert'
]);
exit;
@@ -0,0 +1,148 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$json = $_POST['json'] ?? '';
$slug = $_POST['slug'] ?? '';
$allowed_slugs = ['rankLive-overview' => 'all', 'rankLive-geraet' => 'geraet', 'kampfrichter-admin' => 'all', 'kampfrichter-geraet' => 'geraet', 'rangliste' => 'all'];
if (!array_key_exists($slug, $allowed_slugs)){
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
if (!$json || $json === ''){
echo json_encode(['success' => false, 'message' => 'No input']);
exit;
}
$array = json_decode($json, true) ?? [];
$body = $array['body'];
$all_noten = [];
function extractNotenData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenData($value));
}
}
return $result;
}
function extractNotenGeraetData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenGeraetData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenGeraetData($value));
}
}
return $result;
}
$all_noten = ($allowed_slugs[$slug] === 'all') ? extractNotenData($body) : extractNotenGeraetData($body);
$all_noten_export = [];
foreach ($all_noten as $single_note) {
$all_noten_export[] = [
'g_id' => $single_note['geraet-id'] ?? 'A',
'n_id' => $single_note['field-type-id'],
'r' => $single_note['run']
];
}
function unique_matrix($matrix) {
$matrixAux = $matrix;
foreach($matrix as $key => $subMatrix) {
unset($matrixAux[$key]);
foreach($matrixAux as $subMatrixAux) {
if($subMatrix === $subMatrixAux) {
unset($matrix[$key]);
}
}
}
return $matrix;
}
$all_noten_json = json_encode(unique_matrix($all_noten_export) ?? []) ?? '';
$user_id = (int) $_SESSION['user_id_wk_leitung'] ?? 0;
$stmt = $mysqli->prepare("INSERT INTO `$db_tabelle_tabellen_konfiguration` (`json`, `all_noten_json`, `type_slug`, `created_by`, `updated_by`) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `json` = VALUES(`json`), `all_noten_json` = VAlUES(`all_noten_json`), `updated_by` = VALUES(`updated_by`)");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical DB ERROR']);
exit;
}
$stmt->bind_param("sssii", $json, $all_noten_json, $slug, $user_id, $user_id);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
if (str_contains($slug, 'rankLive')) {
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
}
// Return JSON
echo json_encode([
'success' => true,
'message' => 'gespeichert'
]);
exit;
@@ -0,0 +1,82 @@
<?php
ini_set("display_errors",1);
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$tableJSONVersion = '1.0.0';
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$slug = $_POST['slug'] ?? '';
$allowed_slugs = [
'rankLive-overview' => 'RankLive Overview',
'rankLive-geraet' => 'RankLive einzelnes Gerät',
'kampfrichter-admin' => 'Kampfrichter Admin (alle Geräte)',
'kampfrichter-geraet' => 'Kampfrichter einzelnes Gerät' //, 'rangliste' => 'all'
];
if (!array_key_exists($slug, $allowed_slugs)){
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
$json = db_get_var($mysqli, "SELECT `json` FROM $db_tabelle_tabellen_konfiguration WHERE `type_slug` = ?", [$slug]) ?: '';
try {
$json_array = json_decode($json, true) ?? [];
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Error parsing JSON']);
exit;
}
$wk_name = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);
$exportData = [
'metadata' => [
'erstellt_am' => date("d.m.Y H:i"),
'ersteller' => $_SERVER['HTTP_HOST'],
'erstellt_mit' => 'WKVS Auto-Export',
'wkvs_json_table_version' => '1.0.0',
'titel' => 'WKVS Table für ' . $allowed_slugs[$slug] . ' ' . $wk_name . ' (V 1.0.0)'
],
'data' => $json_array,
'type' => $slug
];
$jsonOutput = json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// 6. Force Browser File Download
$fileName = 'WKVS-table-config-' . date('Y-m-d_H-i') . '-' . $slug . '.conf.wkvs';
header('Content-Type: application/json; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . strlen($jsonOutput));
echo $jsonOutput;
exit;
@@ -0,0 +1,193 @@
<?php
ini_set("display_errors",1);
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($_FILES['configFile']) || $_FILES['configFile']['error'] !== UPLOAD_ERR_OK) {
echo json_encode([
'success' => false,
'message' => 'Keine Config-Datei ausgewählt'
]);
exit;
}
$tableJSONVersion = '1.0.0';
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$slug = $_POST['slug'] ?? '';
$allowed_slugs = ['rankLive-overview' => 'all', 'rankLive-geraet' => 'geraet', 'kampfrichter-admin' => 'Kampfrichter Admin (alle Geräte)', 'kampfrichter-geraet' => 'geraet', 'rangliste' => 'all'];
if (!array_key_exists($slug, $allowed_slugs)){
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
$tmpPath = $_FILES['configFile']['tmp_name'];
$originalName = $_FILES['configFile']['name'];
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$allowedExt = ['json', 'wkvs'];
if (!in_array($extension, $allowedExt, true)) {
echo json_encode(['success' => false, 'message' => 'Falsches Format (Endung)']);
http_response_code(422);
exit;
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($tmpPath);
$allowedMime = ['application/json'];
if (!in_array($mimeType, $allowedMime, true)) {
echo json_encode(['success' => false, 'message' => 'Dateiinhalt ist kein gültiges JSON']);
http_response_code(422);
exit;
}
$JSONcontent = file_get_contents($tmpPath);
$content = json_decode($JSONcontent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
die("Invalid JSON layout.");
}
if (!isset($content['metadata']['wkvs_json_table_version']) || $content['metadata']['wkvs_json_table_version'] !== $tableJSONVersion) {
echo json_encode(['success' => false, 'message' => 'Inkompatible Datei-Version. (V ' . $tableJSONVersion . ' benötigt'. (isset($content['metadata']['wkvs_json_version']) ? ", benutzte Datei-Version: V " . $content['metadata']['wkvs_json_version'] : '') . ')' ]);
unlink($destination);
http_response_code(422);
exit;
}
if (!isset($content['data']) || !is_array($content['data'])) {
echo json_encode(['success' => false, 'message' => 'Ungültige JSON-Struktur']);
unlink($destination);
http_response_code(422);
exit;
}
$body = $content['data']['body'];
$all_noten = [];
function extractNotenData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenData($value));
}
}
return $result;
}
function extractNotenGeraetData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenGeraetData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenGeraetData($value));
}
}
return $result;
}
$all_noten = ($allowed_slugs[$slug] === 'all') ? extractNotenData($body) : extractNotenGeraetData($body);
$all_noten_export = [];
foreach ($all_noten as $single_note) {
$all_noten_export[] = [
'g_id' => $single_note['geraet-id'] ?? 'A',
'n_id' => $single_note['field-type-id'],
'r' => $single_note['run']
];
}
function unique_matrix($matrix) {
$matrixAux = $matrix;
foreach($matrix as $key => $subMatrix) {
unset($matrixAux[$key]);
foreach($matrixAux as $subMatrixAux) {
if($subMatrix === $subMatrixAux) {
unset($matrix[$key]);
}
}
}
return $matrix;
}
$all_noten_json = json_encode(unique_matrix($all_noten_export) ?? []) ?? '';
$user_id = (int) $_SESSION['user_id_wk_leitung'] ?? 0;
$stmt = $mysqli->prepare("INSERT INTO `$db_tabelle_tabellen_konfiguration` (`json`, `all_noten_json`, `type_slug`, `created_by`, `updated_by`) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `json` = VALUES(`json`), `all_noten_json` = VAlUES(`all_noten_json`), `updated_by` = VALUES(`updated_by`)");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical DB ERROR']);
exit;
}
$json = json_encode($content['data']);
$stmt->bind_param("sssii", $json, $all_noten_json, $slug, $user_id, $user_id);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
if (str_contains($slug, 'rankLive')) {
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
}
// Return JSON
echo json_encode([
'success' => true,
'message' => 'gespeichert'
]);
exit;
@@ -0,0 +1,366 @@
<?php
use TCPDF;
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$wkName = db_get_var($mysqli, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);
$wkName = ($wkName === null) ? 'Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen' : $wkName;
$normalFontSize = 11;
$marginSide = 10;
$marginTop = 10;
$minMarginBottomTable = 20;
$format = 'A4';
$orientation = 'L';
$svgs = [
'1' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 2h12v2a6 6 0 0 1-6 6 6 6 0 0 1-6-6V2zm0 20h12v-2a6 6 0 0 0-6-6 6 6 0 0 0-6 6v2zm6-10c1.657 0 3-1.343 3-3H9c0 1.657 1.343 3 3 3z" fill="#0073aa"/></svg>',
'2' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#28a745"/><path d="M7 12l3 3 7-7" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>',
'3' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#dc3545"/><path d="M8 8l8 8M16 8l-8 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
];
$now = trim($_POST['date'] ?? date('Y-m-d H:i:s'));
$logEntries = db_select($mysqli, $db_tabelle_verbuchte_startgebueren_log, "*");
$personen = db_select($mysqli, $db_tabelle_intern_benutzende, "`id`, `name_person`, `username`");
$indexedPersonen = array_column($personen, null, 'id');
$entriesArray = [];
foreach ($logEntries as $slE) {
// $value = ($slE['old_order_status'] !== null) ? ($svgs[$slE['old_order_status']] ?? $slE['old_order_status']) . ' ' . json_decode('"\u2192"') . ' ' . ($svgs[$slE['new_order_status'] ?? ''] ?? $slE['new_order_status'] ?? '') : ($svgs[$slE['new_order_status'] ?? ''] ?? $slE['new_order_status'] ?? '');
$valueArray = [
1 => $svgs[$slE['old_order_status'] ?? ''] ?? '',
2 => isset($svgs[$slE['old_order_status']]),
3 => $svgs[$slE['new_order_status'] ?? ''] ?? '',
4 => isset($svgs[$slE['new_order_status']])
];
$entriesArray[] = [
'order_id' => $slE['order_id'] ?? '',
'value' => $valueArray,
'edited_by' => $indexedPersonen[$slE['edited_by']]['name_person'] ?? 'Unbekannter User',
'timestamp' => new DateTime($slE['edited_at'])->format('d.m.Y H:i:s') ?? '---'
];
}
// Load TCPDF
require $baseDir . '/../composer/vendor/autoload.php';
class ProtokollPDF extends TCPDF
{
public $columns;
public $wkName;
public $marginSide;
public $marginTop;
public $pdfHeight;
public $pdfWidth;
public $headerType;
public $now;
// Page header
public $headerBottomY = 0;
public function Header()
{
$this->SetFillColor(255, 255, 255);
$this->Rect(0, 0, $this->pdfWidth, $this->pdfHeight, 'F');
$arrayTitles = [];
$image_file = $_SERVER['DOCUMENT_ROOT'] . '/intern/img/logo-normal.png';
$this->SetY($this->marginTop);
$this->SetX(($this->marginSide ?? 10));
$this->SetFont('freesans', '', 20);
$this->Cell(0, 0, 'Protokoll Rechnungen ' . $this->wkName, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$preimageX = $this->GetX();
$preimageY = $this->GetY();
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->SetY($preimageY + 5);
$this->SetX($this->marginSide ?? 10); // Force back to left margin
$this->SetFont('freesans', '', 11);
$this->Ln(5);
$columns = $this->columns;
$startY = $this->GetY();
$this->SetFont('', 'B');
$this->SetX($this->marginSide ?? 10);
foreach ($columns as $col) {
$title = $col['header'];
if (($col['rotate'] ?? false)) {
$x = $this->GetX();
$y = $this->GetY() + 7;
$this->StartTransform();
$this->Rotate(45, $x, $y);
$lineY = $startY + 7;
if (($col['id'] ?? '') === 'gesamt') {
$this->SetLineWidth(0.6);
} else {
$this->SetLineWidth(0.2);
}
$this->Line($x, $lineY, $x + ($col['width_header'] ?? 0), $lineY);
$this->SetLineWidth(0.2);
$this->Cell($col['width_header'] ?? 0, 7, $title ?? '', 0, 0, 'C');
$this->StopTransform();
$this->SetX($x + ($col['max_width'] ?? 0));
} else {
$this->Cell($col['max_width'] ?? 0, 7, $title ?? '', 0, 0, $col['align'] ?? 'L');
}
}
$this->Ln();
$this->headerBottomY = $this->GetY();
}
// Page footer
public function Footer()
{
$this->SetLineWidth(0.6);
$this->Line($this->marginSide ?? 10, $this->headerBottomY, $this->pdfWidth - ($this->marginSide ?? 10), $this->headerBottomY);
$this->SetLineWidth(0.2);
$this->SetY(0);
$this->SetX(0);
$this->SetFillColor(255, 255, 255); // white
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(0);
$this->SetX($this->pdfWidth - ($this->marginSide ?? 10));
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(-15);
$this->SetFont('freesans', 'I', 8);
// 1. Page Number - Centered
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C');
// 2. Timestamp - Force to Left
$this->SetX($this->getMargins()['left']); // Reset to left margin
$this->Cell(0, 10, 'Zeitstempel: ' . $this->now, 0, false, 'L');
// 3. Host - Force to Right (Cell with width 0 and align 'R' handles this)
$this->SetX($this->getMargins()['left']); // Reset again so 'R' alignment works from the start
$this->Cell(0, 10, $_SERVER['HTTP_HOST'], 0, 0, 'R', false, 'https://'.$_SERVER['HTTP_HOST']);
}
}
$pdf = new ProtokollPDF($orientation, 'mm', $format, true, 'UTF-8', false);
$pdfHeight = $pdf->getPageHeight();
$pdfWidth = $pdf->getPageWidth();
$pdf->wkName = $wkName;
$pdf->marginSide = $marginSide;
$pdf->marginTop = $marginTop;
$pdf->pdfHeight = $pdfHeight;
$pdf->pdfWidth = $pdfWidth;
$pdf->now = $now;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($wkName);
$pdf->SetTitle($wkName . "_Protokoll.pdf");
$pdf->SetAutoPageBreak(FALSE);
$pdf->SetFont('freesans', '', 11);
// Define columns dynamically
$columns = [
['id' => 'order_id', 'header' => 'Rechnungsnummer', 'align' => 'L', 'flex' => true, 'type' => 'link', 'field' => 'order_id'],
['id' => 'value', 'header' => 'Änderung', 'align' => 'L', 'flex' => true, 'type' => 'value', 'field' => 'value'],
['id' => 'edited_by', 'header' => 'Geändert durch', 'align' => 'C', 'flex' => true, 'type' => 'field', 'field' => 'edited_by'],
['id' => 'timestamp', 'header' => 'Zeitstempel', 'align' => 'C', 'flex' => true, 'type' => 'field', 'field' => 'timestamp'],
];
$padding = 4;
// 1. Calculate initial max widths based on headers
foreach ($columns as &$col) {
$col['width_header'] = $pdf->GetStringWidth($col['header']) + ($padding * 2);
if ($col['type'] !== 'note') {
$col['max_width'] = $col['width_header'];
} else {
$col['max_width'] = 0;
}
}
unset($col);
foreach ($entriesArray as $row) {
foreach ($columns as $col) {
$text = '';
if ($col['type'] !== 'value') {
if (isset($row[$col['field']])) {
$text = $row[$col['field']];
}
$width = $pdf->GetStringWidth($text) + ($padding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
} else {
if (40 > $col['max_width']) {
$col['max_width'] = 40;
}
}
}
}
$fixedWidth = 0;
foreach ($columns as $col) {
if ($col['flex'] ?? false) {
$flexCols[] = $col['id'];
$fixedWidth += $col['max_width'];
} else {
$fixedWidth += $col['max_width'];
}
}
$tablew = $pdfWidth - (2 * $marginSide);
$effTableWidth = $tablew - $fixedWidth;
if (!empty($flexCols) && $effTableWidth > 0) {
$flexTotalInitial = 0;
foreach ($columns as $col) {
if ($col['flex'] ?? false) $flexTotalInitial += $col['max_width'];
}
foreach ($columns as &$col) {
if ($col['flex'] ?? false) {
$col['max_width'] += ($col['max_width'] / $flexTotalInitial) * $effTableWidth;
}
}
unset($col);
}
$pdf->columns = $columns;
$pdf->AddPage();
$margin_top = $pdf->headerBottomY;
$pdf->SetMargins($marginSide, $margin_top, $marginSide); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually
$pdf->SetFont('', '');
foreach ($entriesArray as $row) {
$rowHeight = 10;
foreach ($columns as $col) {
$pdf->setFillColor(255, 255, 255);
if ($pdf->getY() + $rowHeight > $pdfHeight - $minMarginBottomTable) {
$pdf->addPage();
$margin_top = $pdf->headerBottomY;
$pdf->setY($margin_top);
}
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
}
$startX = $pdf->GetX();
$startY = $pdf->GetY();
if ($col['type'] === 'field') {
$text = $row[$col['field']] ?? '';
$pdf->MultiCell($col['max_width'], $rowHeight, $text, 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
} elseif ($col['type'] === 'value') {
$pdf->MultiCell($col['max_width'], $rowHeight, '', 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
$valuesArrary = $row[$col['field']];
if (isset($valuesArrary[1]) && isset($valuesArrary[2]) && $valuesArrary[2]) {
$pdf->ImageSVG('@' . $valuesArrary[1], $startX, $startY + 2, $rowHeight - 4, $rowHeight - 4);
$pdf->MultiCell(10, $rowHeight, json_decode('"\u2192"'), '', 'C', false, 0, $startX + $rowHeight - 4, $startY, true, 0, false, true, $rowHeight, 'M');
}
if (isset($valuesArrary[3]) && isset($valuesArrary[3]) && $valuesArrary[4]) {
$pdf->ImageSVG('@' . $valuesArrary[3], $startX + $rowHeight - 4 + 10, $startY + 2, $rowHeight - 4, $rowHeight - 4);
}
$pdf->SetX($startX + $col['max_width']);
} elseif ($col['type'] === 'link') {
$text = $row[$col['field']] ?? '';
$pdf->MultiCell($col['max_width'], $rowHeight, $text, 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
$pdf->Link($startX, $startY, $col['max_width'], $rowHeight, 'https://'.$_SERVER['HTTP_HOST'].'/intern/wk-leitung/rechnungen-viewer?order_id='.$text);
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->SetY($startY + $rowHeight);
}
$textanzEintaege = (count($entriesArray) > 1) ? 'Einträge': 'Eintrag';
$pdf->SetFont('', '', 7);
$pdf->Cell(20, 6, count($entriesArray).' '.$textanzEintaege, 0, 0, 'L');
$pdf->SetFont('', '', $normalFontSize);
$pdf->Output($wkName . "_Protokoll_Rechnungen.pdf", 'I');
exit;
@@ -0,0 +1,112 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$allowedActions = ['payEntrys' => 2, 'deleteEntrys' => 3];
$requestedAction = trim($_POST['action'] ?? '');
if (!array_key_exists($requestedAction, $allowedActions)) {
echo json_encode(['success' => false, 'message' => 'Invalid Action']);
http_response_code(400);
exit;
}
$actionValue = $allowedActions[$requestedAction];
$user = $_SESSION['user_id_wk_leitung'] ?? 0;
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
http_response_code(500);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
if (!isset($_POST['ids']) || !is_array($_POST['ids']) || count($_POST['ids']) < 1) {
echo json_encode(['success' => false, 'message' => 'Keine Id angegeben']);
http_response_code(422);
exit;
}
$ids = $_POST['ids'];
// Validate: all IDs must be integers
$ids = array_filter($ids, fn($id) => ctype_digit(strval($id)));
if (count($ids) === 0) {
echo json_encode(['success' => false, 'message' => 'Kein gültiger Input']);
http_response_code(422);
exit;
}
// Build placeholders for prepared statement
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$affectedRows = db_select($mysqli, $db_tabelle_verbuchte_startgebueren, "order_status, order_id, `item_ids`, `used_verein_konten`", "order_id IN ($placeholders)", $ids);
$mysqli->begin_transaction();
require $baseDir . '/../scripts/rechnungen/rechnungs-functions.php';
try {
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_verbuchte_startgebueren_log (`old_order_status`, `new_order_status`, `order_id`, `edited_by`) VALUES (?, ?, ?, ?)");
foreach ($affectedRows as $row) {
$stmt->bind_param("ssss", $row['order_status'], $actionValue, $row['order_id'], $user);
$stmt->execute();
update_teilnehmende($row['item_ids'], $row['order_status'], $actionValue);
update_konten_vereine($row['used_verein_konten'], $row['order_status'], $actionValue);
}
// Prepare the SQL statement
$sql = "UPDATE $db_tabelle_verbuchte_startgebueren SET order_status = ? WHERE order_id IN ($placeholders)";
$stmt = $mysqli->prepare($sql);
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Vorbereiten der Abfrage']);
http_response_code(500);
exit;
}
$params = array_merge([$actionValue], $ids);
$types = 's' . str_repeat('i', count($ids));
$stmt->bind_param($types, ...$params);
if (!$stmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Löschen']);
http_response_code(500);
exit;
}
$stmt->close();
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
exit;
}
$mysqli->close();
echo json_encode(['success' => true, 'message' => 'Bestellungen erfolgreich gelöscht']);
http_response_code(200);
exit;
@@ -0,0 +1,87 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
http_response_code(500);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
if (!isset($_POST['scor']) || empty($_POST['scor']) || $_POST['scor'] === '') {
echo json_encode(['success' => false, 'message' => 'Keine Referenz angegeben']);
http_response_code(422);
exit;
}
$user = $_SESSION['user_id_wk_leitung'] ?? 0;
$scorFullStr = strval($_POST['scor'] ?? '');
$scor = intval(substr($scorFullStr, 2));
$stmt = $mysqli->prepare(
"SELECT 1 FROM $db_tabelle_verbuchte_startgebueren WHERE order_id = ? LIMIT 1"
);
$stmt->bind_param('i', $scor);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows !== 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Referenz']);
http_response_code(403);
exit;
}
$stmt->close();
require $baseDir . '/../scripts/rechnungen/rechnungs-functions.php';
$affectedRow = db_select($mysqli, $db_tabelle_verbuchte_startgebueren, "`order_status`, `order_id`, `item_ids`, `used_verein_konten`", "order_id = ?", [$scor]);
try {
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_verbuchte_startgebueren_log (`old_order_status`, `new_order_status`, `order_id`, `edited_by`) VALUES (?, 2, ?, ?)");
foreach ($affectedRow as $row) {
$stmt->bind_param("sss", $row['order_status'], $row['order_id'], $user);
$stmt->execute();
update_teilnehmende($row['item_ids'], $row['order_status'], 2);
update_konten_vereine($row['used_verein_konten'], $row['order_status'], 2);
}
$sql = "UPDATE $db_tabelle_verbuchte_startgebueren SET order_status = 2 WHERE order_id = ?";
$nstmt = $mysqli->prepare($sql);
$nstmt->bind_param('i', $scor);
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
exit;
}
$mysqli->close();
echo json_encode(['success' => true, 'message' => 'Rechnung wurde aktualisiert']);
http_response_code(200);
exit;
@@ -0,0 +1,86 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$action = $_POST['action'] ?? '';
if ($action === 'update') {
$abt_id = intval($_POST['abt'] ?? 0);
$field = $_POST['type'] ?? '';
$type_id = intval($_POST['type_id'] ?? 0);
$value = $_POST['value'] ?? '';
if (!preg_match('/^\d{2}:\d{2}$/', $value)) {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
http_response_code(400);
exit;
}
$allowedFields = ['start', 'end'];
if (
$abt_id > 0 &&
$type_id > 0 &&
db_check_var($mysqli, $db_tabelle_gruppen, 'id = ?', [$abt_id]) &&
db_check_var($mysqli, $db_tabelle_zeitplan_types, 'id = ?', [$type_id]) &&
($field === 'start' || ($field === 'end' && db_check_var($mysqli, $db_tabelle_zeitplan_types, 'id = ? AND has_endtime = ?', [$type_id, 1])))
) {
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_gruppen_zeiten (`abt_id`, `zeitplan_id`, `" . $field . "_time`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `" . $field . "_time` = VALUES(`" . $field . "_time`)");
$stmt->bind_param("iis", $abt_id, $type_id, $value);
$stmt->execute();
$stmt->close();
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
http_response_code(400);
exit;
}
} elseif ($action === 'delete') {
$abt_id = intval($_POST['abt'] ?? 0);
$type_id = intval($_POST['type_id'] ?? 0);
if ($type_id > 0 && $abt_id > 0) {
db_delete($mysqli, $db_tabelle_gruppen_zeiten, ['abt_id' => $abt_id, 'zeitplan_id' => $type_id]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
http_response_code(400);
exit;
}
} else {
echo json_encode(['success' => false, 'message' => 'Action not found.']);
}
@@ -0,0 +1,183 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// Show all errors except deprecation notices (these come from vendor libraries
// that aren't yet typed for newer PHP versions). Long-term fix: update
// dependencies to versions compatible with your PHP runtime.
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$allowed_operations = ['overrite', 'add'];
if (!isset($_POST['operation']) || !in_array($_POST['operation'], $allowed_operations, true)) {
http_response_code(400);
echo 'Invalid operation.';
exit;
}
$operation = $_POST['operation'];
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$allGeraete = db_select($mysqli, $db_tabelle_disziplinen, "id, name, start_index", "", [], "start_index ASC");
$allAbt = db_select($mysqli, $db_tabelle_gruppen, "name, id, order_index", "", [], "order_index ASC");
$indexded_order_index_abt = array_column($allAbt, null, 'order_index');
$maxTurnerinnenGruppe = 8;
$allTurnerinnen = db_select($mysqli, $db_tabelle_teilnehmende, "id, programm, verein, name" , "", [], "programm ASC");
$grouped = [];
foreach ($allTurnerinnen as $t) {
$grouped[strtolower($t['programm'])][$t['verein']][] = $t;
}
function getMinKey(array $array): string|int {
return array_search(min($array), $array, true);
}
function arrayRiegeneiteilung($allturnerinnen, $maxTurnerinnenGruppe, $allGeraete) {
$indabt = 1;
$arrayAutoRiegeneinteilung = [];
foreach ($allturnerinnen as $prog => $vereine) {
// ABTEILUNG BERECHNEN
$countturterinnenprog = 0;
foreach ($vereine as $verein => $turnerinnen) {
foreach ($turnerinnen as $ind => $turnerin) {
$countturterinnenprog++;
$abt = intdiv($countturterinnenprog, $maxTurnerinnenGruppe * count($allGeraete)) + $indabt;
$allturnerinnen[$prog][$verein][$ind]['abt'] = $abt;
$arrayAbt[$abt][$verein][] = $turnerin;
}
}
$indabt += intdiv($countturterinnenprog - 1, $maxTurnerinnenGruppe * count($allGeraete)) + 1;
}
foreach ($arrayAbt as $abt => $vereine) {
// AUSGLEICHEN DER GRUPPEN
$arrayAbt = [];
$arrayGruppen = [];
foreach ($allGeraete as $geraet) {
$arrayGruppen[$geraet['id']] = 0;
}
$turnerinnenCount = 0;
foreach ($vereine as $verein => $turnerinnen) {
$turnerinnenCount += count($turnerinnen);
}
foreach ($vereine as $verein => $turnerinnen) {
if (count($turnerinnen) > $maxTurnerinnenGruppe || count($turnerinnen) > $turnerinnenCount / count($allGeraete)) {
if (count($turnerinnen) > $turnerinnenCount / count($allGeraete)) {
$maxTurnerinnenGruppeTemp = ceil($turnerinnenCount / count($allGeraete));
} else {
$maxTurnerinnenGruppeTemp = $maxTurnerinnenGruppe;
}
// AUFTEILEN IN MEHRERE GRUPPEN
$chunks = array_chunk($turnerinnen, $maxTurnerinnenGruppeTemp);
foreach ($chunks as $chunk) {
$minKey = getMinKey($arrayGruppen);
$arrayGruppen[$minKey] += count($chunk);
foreach ($chunk as $ind =>$turnerin) {
$arrayAutoRiegeneinteilung[$abt][$minKey][] = $turnerin;
}
}
continue;
}
$minKey = getMinKey($arrayGruppen);
$arrayGruppen[$minKey] += count($turnerinnen);
foreach ($turnerinnen as $ind =>$turnerin) {
$arrayAutoRiegeneinteilung[$abt][$minKey][] = $turnerin;
}
}
}
foreach ($arrayAutoRiegeneinteilung as $indAbt => $abt) {
foreach ($abt as $indGeraet => $geraet) {
foreach ($geraet as $indTurnerin => $turnerin) {
$arrayAutoRiegeneinteilung[$indAbt][$indGeraet][$indTurnerin]['turnerin_index'] = $indTurnerin + 1;
}
}
}
return $arrayAutoRiegeneinteilung;
}
$arrayAutoRiegeneinteilung = arrayRiegeneiteilung($grouped, $maxTurnerinnenGruppe, $allGeraete);
if ($operation === 'overrite') {
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_gruppen");
$stmt->execute();
$stmt->close();
}
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_teilnehmende_gruppen");
$stmt->execute();
$stmt->close();
foreach ($arrayAutoRiegeneinteilung as $indAbt => $abt) {
if ($operation === 'overrite' || !isset($indexded_order_index_abt[$indAbt])) {
$var_name = "Abteilung $indAbt";
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_gruppen (`name`, `order_index`) VALUES (?, ?)");
$stmt->bind_param("si", $var_name, $indAbt);
$stmt->execute();
$idAbt = $stmt->insert_id;
$stmt->close();
} else {
$idAbt = $indexded_order_index_abt[$indAbt]['id'];
}
foreach ($abt as $indGeraet => $geraet) {
foreach ($geraet as $indTurnerin => $turnerin) {
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_teilnehmende_gruppen (turnerin_id, abteilung_id, geraet_id, turnerin_index) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssss", $turnerin['id'], $idAbt, $indGeraet, $turnerin['turnerin_index']);
$stmt->execute();
$stmt->close();
}
}
}
return http_response_code(200);

Some files were not shown because too many files have changed in this diff Show More